//-------------------------------------------------------------------------
/*
  Copyright (C) 1996, 2003 - 3D Realms Entertainment

  This file is part of Duke Nukem 3D version 1.5 - Atomic Edition

  Duke Nukem 3D is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  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
  aint32_t with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

  Original Source: 1996 - Todd Replogle
  Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------

#include "SDL.h"

#include "types.h"

#include "develop.h"
#include "scriplib.h"
#include "file_lib.h"
#include "gamedefs.h"
#include "keyboard.h"
#include "util_lib.h"
#include "function.h"
#include "control.h"
#include "sounds.h"
#include "config.h"
#include "audiolib/sndcards.h"

#include "duke3d.h"

#include "console.h"
#include "cvars.h"
#include "cvar_defs.h"

#include "global.h"


#define MINITEXT_BLUE   0
#define MINITEXT_RED    2
#define MINITEXT_YELLOW 23
#define MINITEXT_GRAY   17

#define COLOR_ON  MINITEXT_YELLOW
#define COLOR_OFF MINITEXT_BLUE

#define IDFSIZE 479985668
// #define IDFSIZE 9961476
// #define IDFSIZE 16384
#define IDFILENAME "DUKE3D.IDF"

#define TIMERUPDATESIZ 32

int32_t cameradist = 0, cameraclock = 0;
uint8_t  eightytwofifty = 0;
uint8_t  playerswhenstarted;
uint8_t  qe,cp;

uint8_t  nHostForceDisableAutoaim = 0;

// Game play speed
int g_iTickRate = 120;
int g_iTicksPerFrame = 26;

int32 CommandSoundToggleOff = 0;
int32 CommandMusicToggleOff = 0;

// For addfaz's stun server. use /stun to activate
uint16_t g_bStun = 0;

char confilename[128] = {"GAME.CON"};
char boardfilename[128] = {0};
uint8_t  waterpal[768], slimepal[768], titlepal[768], drealms[768], endingpal[768];
char  firstdemofile[80] = { '\0' };

#define patchstatusbar(x1,y1,x2,y2)                                     \
    {                                                                   \
        rotatesprite(0,(200-34)<<16,65536L,0,BOTTOMSTATUSBAR,4,0,10+16+64+128, \
                     scale(x1,xdim,320),scale(y1,ydim,200),             \
                     scale(x2,xdim,320)-1,scale(y2,ydim,200)-1);        \
    }

void newint24( int errval, int ax, int bp, int si );

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


extern uint8_t  syncstate;
extern int32 numlumps;

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

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

extern int32_t lastvisinc;

void timerhandler(void)
{
    totalclock++;
}

int gametext(int x,int y,char  *t,uint8_t  s,short dabits)
{
    short ac,newx;
    char* oldt;
    uint8_t  centre;

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

    if(centre)
    {
        while(*t)
        {
            if(*t == 32) {newx+=5;t++;continue;}
            else ac = *t - '!' + STARTALPHANUM;

            if( ac < STARTALPHANUM || ac > ENDALPHANUM ) break;

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

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

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

        if( ac < STARTALPHANUM || ac > ENDALPHANUM )
            break;

        rotatesprite(x<<16,y<<16,65536L,0,ac,s,0,dabits,0,0,xdim-1,ydim-1);

        if(*t >= '0' && *t <= '9')
            x += 8;
        else x += tiles[ac].dim.width;

        t++;
    }

    return (x);
}

int gametextpal(int x,int y,char  *t,uint8_t  s,uint8_t  p)
{
    short ac,newx;
    uint8_t  centre;
    char* oldt;

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

    if(centre)
    {
        while(*t)
        {
            if(*t == 32) {newx+=5;t++;continue;}
            else ac = *t - '!' + STARTALPHANUM;

            if( ac < STARTALPHANUM || ac > ENDALPHANUM ) break;

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

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

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

        if( ac < STARTALPHANUM || ac > ENDALPHANUM )
            break;

        rotatesprite(x<<16,y<<16,65536L,0,ac,s,p,2+8+16,0,0,xdim-1,ydim-1);
        if(*t >= '0' && *t <= '9')
            x += 8;
        else x += tiles[ac].dim.width;

        t++;
    }

    return (x);
}

int gametextpart(int x,int y,char  *t,uint8_t  s,short p)
{
    short ac,newx, cnt;
    uint8_t  centre;
    char * oldt;

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

    if(centre)
    {
        while(*t)
        {
            if(cnt == p) break;

            if(*t == 32) {newx+=5;t++;continue;}
            else ac = *t - '!' + STARTALPHANUM;

            if( ac < STARTALPHANUM || ac > ENDALPHANUM ) break;

            newx += tiles[ac].dim.width;
            t++;
            cnt++;

        }

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

    cnt = 0;
    while(*t)
    {
        if(*t == 32) {x+=5;t++;continue;}
        else ac = *t - '!' + STARTALPHANUM;

        if( ac < STARTALPHANUM || ac > ENDALPHANUM ) break;

        if(cnt == p)
        {
            rotatesprite(x<<16,y<<16,65536L,0,ac,s,1,2+8+16,0,0,xdim-1,ydim-1);
            break;
        }
        else
            rotatesprite(x<<16,y<<16,65536L,0,ac,s,0,2+8+16,0,0,xdim-1,ydim-1);

        x += tiles[ac].dim.width;

        t++;
        cnt++;
    }

    return (x);
}

int minitext(int x,int y,char  *str,uint8_t  p,uint8_t  sb)
{
    short ac;
    char  buf[128];
    char  *t;

    strncpy (buf, str, 128);
    buf[127] = 0;
    t = buf;

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

        rotatesprite(x<<16,y<<16,65536L,0,ac,0,p,sb,0,0,xdim-1,ydim-1);
        x += 4; // tilesizx[ac]+1;

        t++;
    }
    return (x);
}

int minitextshade(int x,int y,char  *str,uint8_t  s,uint8_t  p,uint8_t  sb)
{
    short ac;
    char  buf[128];
    char  *t;

    strncpy (buf, str, 128);
    buf[127] = 0;
    t = buf;

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

        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);
}

void gamenumber(int32_t x,int32_t y,int32_t n,uint8_t  s)
{
    char  b[10];


    //
    // uint8_t  * ltoa(int32_t l, uint8_t  * buffer, int radix);
    // is NON-STANDARD and equivalent to STANDARD
    // (void) sprintf(buffer, "%ld", l);
    //ltoa(n,b,10);
    sprintf(b,"%d",n);
    gametext(x,y,b,s,2+8+16);
}


char  recbuf[80];
void allowtimetocorrecterrorswhenquitting(void)
{
    int32_t i, j, oldtotalclock;

    ready2send = 0;

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

        while (totalclock < oldtotalclock+TICSPERFRAME)
            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);
    }
}

#define MAXUSERQUOTES 4
int32_t quotebot, quotebotgoal;
short user_quote_time[MAXUSERQUOTES];
char  user_quote[MAXUSERQUOTES][128];
// uint8_t  typebuflen,typebuf[41];

static void adduserquote(char  *daquote)
{
    int32_t 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);
    user_quote_time[0] = 180;
    pub = NUMPAGES;
}

char  *grpVersion2char_from_crc(unsigned int crc32_grp_to_identify)
{
    char  *id;
    int i=0;

    id = crc32lookup[MAX_KNOWN_GRP].name; // unknown version

    for(i=0; i<MAX_KNOWN_GRP; i++)
    {
        if(crc32lookup[i].crc32==crc32_grp_to_identify)
            id = crc32lookup[i].name;
    }

    return(id);
}

char  *grpVersion2char(uint8_t  grp_to_identify)
{
    char  *id;

    switch(grp_to_identify)
    {
    case DUKEITOUTINDC_GRP:
        id = "v1.5 DC PACK";
        break;
    case SHAREWARE_GRP13:
        id = "v1.3 SHAREW.";
        break;
    case ATOMIC_GRP14_15:
        id = "v1.5 ATOMIC";
        break;
    case REGULAR_GRP13D:
        id = "v1.3D FULL";
        break;
    case UNKNOWN_GRP:
        id = "vX.X UNKNOWN";
        break;
    default:
        Error(EXIT_FAILURE,"Failed the GRP Identification\n");
        break;
    }

    return(id);
}

//This is a function from the Engine module, used in getpackets.
void sampletimer(void);

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

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

    // not a net game
    if (numplayers < 2)
    {
        //printf("getpackets() numplayers < 2");
        return;
    }

    while ((packbufleng = getpacket(&other,packbuf)) > 0)
    {
#ifdef _DEBUG_NETWORKING_
        printf("RECEIVED PACKET: type: %d : len %d\n", packbuf[0], packbufleng);
#endif

        switch(packbuf[0])
        {
        case 253:
            // This should have already been handled by mmulti.cpp so ignore it
            printf("Invalid Packet: %d", packbuf[0]);
            break;

        case 125:
            cp = 0;
            break;

        case 126:
            multiflag = 2;
            multiwhat = 0;
            multiwho = other;
            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 = (int32_t)((int8_t  )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];
            for(i=connecthead;i>=0;i=connectpoint2[i])
            {
                if (playerquitflag[i] == 0) continue;

                l = packbuf[k++];
                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); 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 = (int8_t  )packbuf[j++];
                if (l&8)   nsyn[i].bits = ((nsyn[i].bits&0xffffff00)|((int32_t)packbuf[j++]));
                if (l&16)  nsyn[i].bits = ((nsyn[i].bits&0xffff00ff)|((int32_t)packbuf[j++])<<8);
                if (l&32)  nsyn[i].bits = ((nsyn[i].bits&0xff00ffff)|((int32_t)packbuf[j++])<<16);
                if (l&64)  nsyn[i].bits = ((nsyn[i].bits&0x00ffffff)|((int32_t)packbuf[j++])<<24);
                if (l&128) nsyn[i].horz = (int8_t  )packbuf[j++];

                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 = 2; k = packbuf[1];

            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 = (int8_t  )packbuf[j++];
            if (k&8)   nsyn[other].bits = ((nsyn[other].bits&0xffffff00)|((int32_t)packbuf[j++]));
            if (k&16)  nsyn[other].bits = ((nsyn[other].bits&0xffff00ff)|((int32_t)packbuf[j++])<<8);
            if (k&32)  nsyn[other].bits = ((nsyn[other].bits&0xff00ffff)|((int32_t)packbuf[j++])<<16);
            if (k&64)  nsyn[other].bits = ((nsyn[other].bits&0x00ffffff)|((int32_t)packbuf[j++])<<24);
            if (k&128) nsyn[other].horz = (int8_t  )packbuf[j++];
            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: // message talk T
            strcpy(recbuf,(char*)packbuf+1);
            recbuf[packbufleng-1] = 0;

            adduserquote(recbuf);
            sound(EXITMENUSOUND);

            pus = NUMPAGES;
            pub = NUMPAGES;

            break;

        case 5:
            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];

            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;

            enterlevel(MODE_GAME);

            break;

        case 6: // get names
            for (i=2;packbuf[i] && i<=11;i++) // limit size of name
                ud.user_name[other][i-2] = packbuf[i];
            ud.user_name[other][i-2] = 0;

            // we allow the old rancidmeat 19.1 to connect, using the old grpVersion system w/ BYTEVERSION
            if(packbuf[1] == BYTEVERSION_27 || packbuf[1] == BYTEVERSION_117)
            {
                // Old rancid was using either BYTEVERSION_27 or BYTEVERSION_117
                Error(EXIT_SUCCESS,     "STOP: Your opponent is using an obsolete version\n"
                      "Please ask him to update to xDuke v%d.%d!\n", CHOCOLATE_DUKE_REV_X, CHOCOLATE_DUKE_REV_DOT_Y);
            }
            break;

        case 9:
            for (i=1;i<packbufleng;i++)
                ud.wchoice[other][i-1] = packbuf[i];
            break;

        case 7:

            if(numlumps == 0) break;

            if (SoundToggle == 0 || ud.lockout == 1 || FXDevice == NumSoundCards)
                break;
            rtsptr = 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 8:
            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 = ud.coop = packbuf[8];
            ud.m_marker = ud.marker = packbuf[9];
            ud.m_ffire = ud.ffire = packbuf[10];

            copybufbyte(packbuf+10,boardfilename,packbufleng-11);
            boardfilename[packbufleng-11] = 0;

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

            newgame(ud.volume_number,ud.level_number,ud.player_skill);
            enterlevel(MODE_GAME);
            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 = (int32_t)((int8_t  )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++];
            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 = (int8_t  )packbuf[j++];
            if (k&8)   nsyn[other].bits = ((nsyn[other].bits&0xffffff00)|((int32_t)packbuf[j++]));
            if (k&16)  nsyn[other].bits = ((nsyn[other].bits&0xffff00ff)|((int32_t)packbuf[j++])<<8);
            if (k&32)  nsyn[other].bits = ((nsyn[other].bits&0xff00ffff)|((int32_t)packbuf[j++])<<16);
            if (k&64)  nsyn[other].bits = ((nsyn[other].bits&0x00ffffff)|((int32_t)packbuf[j++])<<24);
            if (k&128) nsyn[other].horz = (int8_t  )packbuf[j++];
            movefifoend[other]++;

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

            if (j > packbufleng)
            {
                printf("INVALID GAME PACKET!!! (%d too many bytes) (j= %d, packbuflen= %d, type: %d)\n",j-packbufleng, j, packbufleng, packbuf[0]);
            }

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

            break;
        case 127:
            break;

#ifdef CHECK_XDUKE_REV
        case 131: // xDuke Rev ID
            memcpy(ud.rev[other], packbuf, 10);
            break;
#endif

        case 132: // get map CRC of opponents (to debug out of synch)
            ud.mapCRC[other] = (uint16_t)packbuf[1] + (uint16_t)(packbuf[2]<<8);
            break;

        case 133: // client refused to disable the autoaim by host

            Error(EXIT_SUCCESS,     "One or more players refused to play with AutoAim OFF because this breaks\n"
                  "the official Duke's gameplay. Please restart without this option...\n");

            break;

        case 134: // Get GRP CRC32 + Con size + exeCRC + conCRC
            memcpy(ud.groupefil_crc32[other], packbuf+1, sizeof(groupefil_crc32));
            memcpy(ud.conSize+other, packbuf+1+sizeof(groupefil_crc32), sizeof(ud.conSize[0]));
            memcpy(ud.conCRC+other, packbuf+1+sizeof(groupefil_crc32)+sizeof(ud.conSize[0]), sizeof(ud.conCRC[0]));
            memcpy(ud.exeCRC+other, packbuf+1+sizeof(groupefil_crc32)+sizeof(ud.conSize[0])+sizeof(ud.conCRC[0]), sizeof(ud.exeCRC[0]));
            break;

        case 250:
        {
            playerreadyflag[other]++;
            printf("Player %d '%s' is ready...\n", other, ud.user_name[other]);
        }
        break;
        case 255:
            gameexitanycase();
            break;
        }
    }
    rb->yield();
}

//From player.c
void computergetinput(int32_t snum, input *syn);
void faketimerhandler()
{
    int32_t i, j, k;
    input *osyn, *nsyn;

    //Check if we should quit the game.
    if ((qe == 0 && KB_KeyPressed(sc_LeftControl) && KB_KeyPressed(sc_LeftAlt) && KB_KeyPressed(sc_Delete)) ||
        (qe == 0 && KB_KeyPressed(sc_LeftAlt) && KB_KeyPressed(sc_F4)))
    {
        qe = 1;
        gameexit("Quick Exit.");
    }

    //Has it been 120ticks ?
    if ((totalclock < ototalclock+TICSPERFRAME) || (ready2send == 0))
        return; // Returns here when playing a demo.

    //YES : Add 120tick
    ototalclock += TICSPERFRAME;

    //Check network stuff.
    getpackets();
    if (getoutputcirclesize() >= 16)
        return;


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

    if( !CONSOLE_IsActive())
    {
        getinput(myconnectindex);
    }

    avgfvel += loc.fvel; // x
    avgsvel += loc.svel; // y
    avgavel += loc.avel;
    avghorz += loc.horz;
    avgbits |= loc.bits;
    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;
    avgfvel = avgsvel = avgavel = avghorz = avgbits = 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]);
                                      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;

        if (nsyn[0].fvel != osyn[0].fvel)
        {
            packbuf[j++] = (uint8_t )nsyn[0].fvel;
            packbuf[j++] = (uint8_t )(nsyn[0].fvel>>8);
            packbuf[k] |= 1;
        }
        if (nsyn[0].svel != osyn[0].svel)
        {
            packbuf[j++] = (uint8_t )nsyn[0].svel;
            packbuf[j++] = (uint8_t )(nsyn[0].svel>>8);
            packbuf[k] |= 2;
        }
        if (nsyn[0].avel != osyn[0].avel)
        {
            packbuf[j++] = (int8_t  )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++] = (uint8_t )nsyn[0].horz;
            packbuf[k] |= 128;
        }

        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; j = 2;

        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++] = (uint8_t )nsyn[0].fvel;
            packbuf[j++] = (uint8_t )(nsyn[0].fvel>>8);
            packbuf[1] |= 1;
        }
        if (nsyn[0].svel != osyn[0].svel)
        {
            packbuf[j++] = (uint8_t )nsyn[0].svel;
            packbuf[j++] = (uint8_t )(nsyn[0].svel>>8);
            packbuf[1] |= 2;
        }
        if (nsyn[0].avel != osyn[0].avel)
        {
            packbuf[j++] = (int8_t  )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++] = (uint8_t )nsyn[0].horz;
            packbuf[1] |= 128;
        }

        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];
        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++] = (uint8_t )nsyn[i].fvel;
                packbuf[j++] = (uint8_t )(nsyn[i].fvel>>8);
                packbuf[k] |= 1;
            }
            if (nsyn[i].svel != osyn[i].svel)
            {
                packbuf[j++] = (uint8_t )nsyn[i].svel;
                packbuf[j++] = (uint8_t )(nsyn[i].svel>>8);
                packbuf[k] |= 2;
            }
            if (nsyn[i].avel != osyn[i].avel)
            {
                packbuf[j++] = (int8_t  )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++] = (uint8_t )nsyn[i].horz;
                packbuf[k] |= 128;
            }
            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 int32_t cacnum;

typedef struct {
    uint8_t *hand;
    int32_t leng;
    uint8_t  *lock; }
    cactype;
extern cactype cac[];

void caches(void)
{
    short i,k;
    char text[512];

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

    k += 6;

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


}

// FIX_00024: A key can be assigned to the new SHOW_INFO function. Display map CRC when
//             in deathmatch. Usefull to identify who loaded a wrong map in multiplayer.
void dispVersion(void)
{
    int i;
    int offx, offy, stepx, stepy;
    char text[512];

    offx = 21; offy = 30;
    stepx = 73; stepy = 20;

    // black translucent background underneath lists
    rotatesprite(0<<16, 0<<16, 65536l<<5, 0, BLANK, 8, 0, 1+2+8+16+64,
                 scale(0,xdim,320),scale(26,ydim,200),
                 scale(320-0,xdim,320)-1,scale(200-((ud.multimode>4)?(161-1*7)-stepy:(161-1*7)),ydim,200)-1);

    // FIX_00009: Show map CRC and GRP file version of each player in case of Out Of Synch
    for(i=connecthead;i>=0;i=connectpoint2[i])
    {
        // Disp name
        sprintf(text,"%s", ud.user_name[i]);
        minitext(offx+(stepx*(i&3)),offy+0+((i&4)>>2)*stepy, text, sprite[ps[i].i].pal, 2+8+16);

        // Disp MAP CRC
        if(ps[i].fakeplayer)
            sprintf(text,"MAP CRC: (bot)");
        else
            sprintf(text,"MAP CRC: %X", ud.mapCRC[i]);
        minitext(offx+(stepx*(i&3)),offy+7+((i&4)>>2)*stepy, text, COLOR_ON,2+8+16);

    }
}

void checksync(void)
{
    int32_t 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)
    {
        minitext(21,30+35+30, "Out Of Sync - Please restart game", COLOR_ON,2+8+16);
        // FIX_00090: Removed info key. FPS were shown after CRC msg. CRC not always removed. (Turrican)
        for(i=connecthead;i>=0;i=connectpoint2[i])
        {
            if (ud.mapCRC[connecthead]!=ud.mapCRC[i])
            {
                minitext(21,30+42+30, "Map CRC mismatching. Please use exactly the same map.", COLOR_ON,2+8+16);
                dispVersion();
            }
            else
                minitext(21,30+42+30, "Verify the con files. Close your P2P if any", COLOR_ON,2+8+16);

        }
    }

    if (syncstate)
    {
        //printext256(4L,160L,31,0,"Missed Network packet!",0);
        //printext256(4L,138L,31,0,"RUN DN3DHELP.EXE for information.",0);
        minitext(21,30+35+30, "Missed Network packet!", COLOR_ON,2+8+16);
    }

}


void check_fta_sounds(short i)
{
    if(sprite[i].extra > 0) switch(PN)
                            {
                            case LIZTROOPONTOILET:
                            case LIZTROOPJUSTSIT:
                            case LIZTROOPSHOOT:
                            case LIZTROOPJETPACK:
                            case LIZTROOPDUCKING:
                            case LIZTROOPRUNNING:
                            case LIZTROOP:
                                spritesound(PRED_RECOG,i);
                                break;
                            case LIZMAN:
                            case LIZMANSPITTING:
                            case LIZMANFEEDING:
                            case LIZMANJUMP:
                                spritesound(CAPT_RECOG,i);
                                break;
                            case PIGCOP:
                            case PIGCOPDIVE:
                                spritesound(PIG_RECOG,i);
                                break;
                            case RECON:
                                spritesound(RECO_RECOG,i);
                                break;
                            case DRONE:
                                spritesound(DRON_RECOG,i);
                                break;
                            case COMMANDER:
                            case COMMANDERSTAYPUT:
                                spritesound(COMM_RECOG,i);
                                break;
                            case ORGANTIC:
                                spritesound(TURR_RECOG,i);
                                break;
                            case OCTABRAIN:
                            case OCTABRAINSTAYPUT:
                                spritesound(OCTA_RECOG,i);
                                break;
                            case BOSS1:
                                sound(BOS1_RECOG);
                                break;
                            case BOSS2:
                                if(sprite[i].pal == 1)
                                    sound(BOS2_RECOG);
                                else sound(WHIPYOURASS);
                                break;
                            case BOSS3:
                                if(sprite[i].pal == 1)
                                    sound(BOS3_RECOG);
                                else sound(RIPHEADNECK);
                                break;
                            case BOSS4:
                            case BOSS4STAYPUT:
                                if(sprite[i].pal == 1)
                                    sound(BOS4_RECOG);
                                sound(BOSS4_FIRSTSEE);
                                break;
                            case GREENSLIME:
                                spritesound(SLIM_RECOG,i);
                                break;
                            }
}

short inventory(spritetype *s)
{
    switch(s->picnum)
    {
    case FIRSTAID:
    case STEROIDS:
    case HEATSENSOR:
    case BOOTS:
    case JETPACK:
    case HOLODUKE:
    case AIRTANK:
        return 1;
    }
    return 0;
}


short badguy(spritetype *s)
{

    switch(s->picnum)
    {
    case SHARK:
    case RECON:
    case DRONE:
    case LIZTROOPONTOILET:
    case LIZTROOPJUSTSIT:
    case LIZTROOPSTAYPUT:
    case LIZTROOPSHOOT:
    case LIZTROOPJETPACK:
    case LIZTROOPDUCKING:
    case LIZTROOPRUNNING:
    case LIZTROOP:
    case OCTABRAIN:
    case COMMANDER:
    case COMMANDERSTAYPUT:
    case PIGCOP:
    case EGG:
    case PIGCOPSTAYPUT:
    case PIGCOPDIVE:
    case LIZMAN:
    case LIZMANSPITTING:
    case LIZMANFEEDING:
    case LIZMANJUMP:
    case ORGANTIC:
    case BOSS1:
    case BOSS2:
    case BOSS3:
    case BOSS4:
    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:
    case ROTATEGUN:
        return 1;
    }
    if( actortype[s->picnum] ) return 1;

    return 0;
}


short badguypic(short pn)
{

    switch(pn)
    {
    case SHARK:
    case RECON:
    case DRONE:
    case LIZTROOPONTOILET:
    case LIZTROOPJUSTSIT:
    case LIZTROOPSTAYPUT:
    case LIZTROOPSHOOT:
    case LIZTROOPJETPACK:
    case LIZTROOPDUCKING:
    case LIZTROOPRUNNING:
    case LIZTROOP:
    case OCTABRAIN:
    case COMMANDER:
    case COMMANDERSTAYPUT:
    case PIGCOP:
    case EGG:
    case PIGCOPSTAYPUT:
    case PIGCOPDIVE:
    case LIZMAN:
    case LIZMANSPITTING:
    case LIZMANFEEDING:
    case LIZMANJUMP:
    case ORGANTIC:
    case BOSS1:
    case BOSS2:
    case BOSS3:
    case BOSS4:
    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:
    case ROTATEGUN:
        return 1;
    }

    if( actortype[pn] ) return 1;

    return 0;
}



void myos(int32_t x, int32_t y, short tilenum, int8_t shade, uint8_t  orientation)
{
    uint8_t  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(int32_t x, int32_t y, short tilenum, int8_t shade, uint8_t  orientation, uint8_t  p)
{
//    uint8_t  fp;
    short a;

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

//    fp = sector[ps[screenpeek].cursectnum].floorpal;

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

}

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

void orderweaponnum(short ind,int32_t x,int32_t y,int32_t num1, int32_t num2,uint8_t  ha)
{
    rotatesprite((x-7)<<16,y<<16,65536L,0,THREEBYFIVE+ind+1,ha-10,7,10+128,0,0,xdim-1,ydim-1);
    rotatesprite((x-3)<<16,y<<16,65536L,0,THREEBYFIVE+10,ha,0,10+128,0,0,xdim-1,ydim-1);

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

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

    rotatesprite((x-7)<<16,y<<16,65536L,0,THREEBYFIVE+ind+1,ha-10,7,10+128,0,0,xdim-1,ydim-1);
    rotatesprite((x-3)<<16,y<<16,65536L,0,THREEBYFIVE+10,ha,0,10+128,0,0,xdim-1,ydim-1);
    rotatesprite((x+9)<<16,y<<16,65536L,0,THREEBYFIVE+11,ha,0,10+128,0,0,xdim-1,ydim-1);

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

    sprintf(dabuf,"%d",num1);
    if(num1 > 9)
    {
        rotatesprite((x)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
        rotatesprite((x+4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
    }
    else rotatesprite((x+4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);

    sprintf(dabuf,"%d",num2);
    if(num2 > 9)
    {
        rotatesprite((x+13)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
        rotatesprite((x+17)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
    }
    else rotatesprite((x+13)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
}

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

    rotatesprite((x-7)<<16,y<<16,65536L,0,THREEBYFIVE+ind+1,ha-10,7,10+128,0,0,xdim-1,ydim-1);
    rotatesprite((x-4)<<16,y<<16,65536L,0,THREEBYFIVE+10,ha,0,10+128,0,0,xdim-1,ydim-1);
    rotatesprite((x+13)<<16,y<<16,65536L,0,THREEBYFIVE+11,ha,0,10+128,0,0,xdim-1,ydim-1);

    sprintf(dabuf,"%d",num1);
    if(num1 > 99)
    {
        rotatesprite((x)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
        rotatesprite((x+4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
        rotatesprite((x+8)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[2]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
    }
    else if(num1 > 9)
    {
        rotatesprite((x+4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
        rotatesprite((x+8)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
    }
    else rotatesprite((x+8)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);

    sprintf(dabuf,"%d",num2);
    if(num2 > 99)
    {
        rotatesprite((x+17)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
        rotatesprite((x+21)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
        rotatesprite((x+25)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[2]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
    }
    else if(num2 > 9)
    {
        rotatesprite((x+17)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
        rotatesprite((x+21)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
    }
    else rotatesprite((x+25)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
}


//REPLACE FULLY
void weapon_amounts(struct player_struct *p,int32_t x,int32_t y,int32_t u)
{
    int cw;

    cw = p->curr_weapon;

    if (u&4)
    {
        if (u != 0xffffffff) patchstatusbar(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 != 0xffffffff) patchstatusbar(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 != 0xffffffff) patchstatusbar(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 != 0xffffffff) patchstatusbar(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 != 0xffffffff) patchstatusbar(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 != 0xffffffff) patchstatusbar(135,190,135+8,190+6);

        if(VOLUMEONE)
        {
            orderweaponnum(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) );
        }
        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 != 0xffffffff) patchstatusbar(166,178,166+8,178+6);

        if(VOLUMEONE)
        {
            orderweaponnum(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) );
        }
        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 != 0xffffffff) patchstatusbar(166,184,166+8,184+6);
        if(VOLUMEONE)
        {
            orderweaponnum(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) );
        }
        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 != 0xffffffff) patchstatusbar(166,190,166+8,190+6);
        if(VOLUMEONE)
        {
            orderweaponnum(-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) );
        }
        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(int32_t x,int32_t y,int32_t n,uint8_t  s,uint8_t  cs)
{
    short i, j, k, p, c;
    char  b[10];

    //
    // uint8_t  * ltoa(int32_t l, uint8_t  * buffer, int radix);
    // is NON-STANDARD and equivalent to STANDARD
    // (void) sprintf(buffer, "%ld", l);
    //ltoa(n,b,10);
    sprintf(b,"%d",n);

    i = strlen(b);
    j = 0;

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

    j = 0;
    for(k=0;k<i;k++)
    {
        p = DIGITALNUM+*(b+k)-'0';
        rotatesprite((c+j)<<16,y<<16,65536L,0,p,s,0,cs,0,0,xdim-1,ydim-1);
        j += tiles[p].dim.width+1;
    }
}

/*

  void scratchmarks(int32_t x,int32_t y,int32_t n,uint8_t  s,uint8_t  p)
  {
  int32_t 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 = 172;

    if(ud.screen_size == 4)
    {
        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;
    char text[512];

    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+128,0,0,xdim-1,ydim-1);
    if(j >= 4) rotatesprite(319,(8)<<16,65600L,0,FRAGBAR,0,0,10+16+64+128,0,0,xdim-1,ydim-1);
    if(j >= 8) rotatesprite(319,(16)<<16,65600L,0,FRAGBAR,0,0,10+16+64+128,0,0,xdim-1,ydim-1);
    if(j >= 12) rotatesprite(319,(24)<<16,65600L,0,FRAGBAR,0,0,10+16+64+128,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,2+8+16+128);
        sprintf(text,"%d",ps[i].frag-ps[i].fraggedself);
        minitext(17+50+(73*(i&3)),2+((i&28)<<1),text,sprite[ps[i].i].pal,2+8+16+128);
    }
}

void display_boardfilename_FPS_weapon(short *offx, short *offy, short *stepx, short *stepy)
{

    short i;

    // FIX_00025: Can toggle FPS and map name during a game (use dnrate OR toggle
    //            from menu when in deathmatch).

    // Display boardfilename and FPS
    if(ud.tickrate&1)
    {
        duke_tics(*offx, *offy, COLOR_ON);
        *offy += *stepy;
    }
    if(ud.tickrate&2)
        dispVersion();

    // We display the weapons here instead of changing the function
    // displayweapon() because the display will be much faster
    for(i=connecthead;i>=0;i=connectpoint2[i])
    {
        if (ud.hideweapon && i==screenpeek)
            drawsmallweapon(ps[i].curr_weapon, 1, 130, (ud.screen_size<=4)?170:140);
    }
}


// FIX_00026: Weapon can now be hidden (on your screen only).
void drawsmallweapon(short weapon, float scale, short x, short y)
{
    float t = 60000;
    int s;
    float offsetx, offsety;


    switch(weapon)
    {
    case  KNEE_WEAPON                       : s=0;                                  break;
    case  PISTOL_WEAPON                     : s=FIRSTGUNSPRITE;
        offsetx = 8;
        offsety = 7;
        break;
    case  SHOTGUN_WEAPON            : s=SHOTGUNSPRITE;
        t = 45000;
        offsetx = -1;
        offsety = 9;
        break;
    case  CHAINGUN_WEAPON           : s=CHAINGUNSPRITE;
        t = 45000;
        offsetx = -1;
        offsety = 9;
        break;
    case  RPG_WEAPON                        : s=RPGSPRITE;
        t = 45000;
        offsetx = 4;
        offsety = 9;
        break;
    case  HANDBOMB_WEAPON           : s=HEAVYHBOMB;
        t=20000;
        offsetx = 16;
        offsety = 13;
        break;
    case  SHRINKER_WEAPON           : s=SHRINKERSPRITE;
        t = 30000;
        offsetx = 6;
        offsety = 14;
        break;
    case  DEVISTATOR_WEAPON         : s=DEVISTATORSPRITE;
        t = 45000;
        offsetx = 3;
        offsety = 9;
        break;
    case  TRIPBOMB_WEAPON           : s=TRIPBOMBSPRITE;
        t = 75000;
        offsetx = 10;
        offsety = 12;
        break;
    case  FREEZE_WEAPON                     :       s=FREEZESPRITE;
        t = 45000;
        offsetx = 1;
        offsety = 6;
        break;
    case  HANDREMOTE_WEAPON         : s=0;
        break;
    case  GROW_WEAPON                       : s=GROWSPRITEICON;
        t = 30000;
        offsetx = 6;
        offsety = 4;
        break;
    default                                         : s=0;
    }

    if(s)
        rotatesprite((x+(short)(offsetx*scale))<<16,(y+(short)(offsety*scale))<<16,(int)(t*scale),0,s,0,0,2+8+16,0,0,xdim-1,ydim-1);

    return;
}

void coolgaugetext(short snum)
{
    struct player_struct *p;
    int32_t i, j, o, ss, u;
    uint8_t  permbit;
    short offx = 3, offy = 3, stepx=60, stepy=6;
    char text[512];

    p = &ps[snum];

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


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

    offy += countfragbars(); //add fragbars
    display_boardfilename_FPS_weapon(&offx, &offy, &stepx, &stepy);


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

    // Draw the multi player frag status bar
    if ( ud.multimode > 1 && ud.coop != 1 )
    {
        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:
    {
        // FIX_00027: Added an extra small statusbar (HUD)
        if(ud.extended_screen_size>0)
        {
            offx = 5; offy = 160;

            sprintf(text,"%d", ps[screenpeek].ammo_amount[ps[screenpeek].curr_weapon]);
            minitext(offx+26,offy+21,text,COLOR_ON,2+8+16); //minitext: 2 red light, 23 yellow
            sprintf(text,"%d", ps[screenpeek].last_extra);
            gametext(offx,offy+20,text,ps[screenpeek].last_extra<=50?15:0,2+8+16); //minitext: 2 red light, 23 yellow
            rotatesprite((offx+0*10)<<16,(offy+28)<<16,20000,0,SHIELD,ps[screenpeek].shield_amount?25:100,0,2+8+16,0,0,xdim-1,ydim-1);
            rotatesprite((offx+0*10)<<16,(offy+28)<<16,ksqrt(ps[screenpeek].shield_amount)*20000/10,0,SHIELD,0,0,2+8+16,0,0,xdim-1,ydim-1);
            rotatesprite((offx+1*10)<<16,(offy+28)<<16,35000,0,JETPACK_ICON,ps[screenpeek].jetpack_amount?25:100,0,2+8+16,0,0,xdim-1,ydim-1);
            rotatesprite((offx+1*10)<<16,(offy+28)<<16,ksqrt(ps[screenpeek].jetpack_amount)*35000/40,0,JETPACK_ICON,0,0,2+8+16,0,0,xdim-1,ydim-1);
            rotatesprite((offx+2*10-1)<<16,(offy+28)<<16,35000,0,STEROIDS_ICON,ps[screenpeek].steroids_amount?25:100,0,2+8+16,0,0,xdim-1,ydim-1);
            rotatesprite((offx+2*10-1)<<16,(offy+28)<<16,ksqrt(ps[screenpeek].steroids_amount)*35000/20,0,STEROIDS_ICON,5,0,2+8+16,0,0,xdim-1,ydim-1);
            rotatesprite((offx+3*10-3)<<16,(offy+28)<<16,40000,0,FIRSTAID_ICON,ps[screenpeek].firstaid_amount?25:100,0,2+8+16,0,0,xdim-1,ydim-1);
            rotatesprite((offx+3*10-3)<<16,(offy+28)<<16,ksqrt(ps[screenpeek].firstaid_amount)*40000/10,0,FIRSTAID_ICON,0,0,2+8+16,0,0,xdim-1,ydim-1);
        }
        else
        {
            if (p->inven_icon)
                rotatesprite(69<<16,(200-30)<<16,65536L,0,INVENTORYBOX,0,21,10+16,0,0,xdim-1,ydim-1);
            rotatesprite(5<<16,(200-28)<<16,65536L,0,HEALTHBOX,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(37<<16,(200-28)<<16,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((231-o)<<16,(200-21)<<16,65536L,0,i,0,0,10+16+permbit,0,0,xdim-1,ydim-1);

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

                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,(uint8_t )i,0,10+permbit);
                if (j > 0) minitext(288-30-o,180,"ON",0,10+16+permbit);
                else if (j != 0x80000000) minitext(284-30-o,180,"OFF",2,10+16+permbit);
                if (p->inven_icon >= 6) minitext(284-35-o,180,"AUTO",2,10+16+permbit);
            }
        }
        return;
    }

    //DRAW/UPDATE FULL STATUS BAR:

    if (pus) { pus = 0; u = 0xffffffff; } 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; }
    if (sbar.shield_amount != p->shield_amount) { sbar.shield_amount = p->shield_amount; 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 < 10;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 == 0xffffffff)
    {
        patchstatusbar(0,0,320,200);
        if (ud.multimode > 1 && ud.coop != 1)
            rotatesprite(277<<16,(200-27)<<16,65536L,0,KILLSICON,0,0,10+16+128,0,0,xdim-1,ydim-1);
    }
    if (ud.multimode > 1 && ud.coop != 1)
    {
        if (u&32768)
        {
            if (u != 0xffffffff) patchstatusbar(276,183,299,193);
            digitalnumber(287,200-17,max(p->frag-p->fraggedself,0),-16,10+16+128);
        }
    }
    else
    {
        if (u&16384)
        {
            if (u != 0xffffffff) patchstatusbar(275,182,299,194);
            if (p->got_access&4) rotatesprite(275<<16,182<<16,65536L,0,ACCESS_ICON,0,23,10+16+128,0,0,xdim-1,ydim-1);
            if (p->got_access&2) rotatesprite(288<<16,182<<16,65536L,0,ACCESS_ICON,0,21,10+16+128,0,0,xdim-1,ydim-1);
            if (p->got_access&1) rotatesprite(281<<16,189<<16,65536L,0,ACCESS_ICON,0,0,10+16+128,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 != 0xffffffff) patchstatusbar(20,183,43,193);
        if(sprite[p->i].pal == 1 && p->last_extra < 2)
            digitalnumber(32,200-17,1,-16,10+16+128);
        else digitalnumber(32,200-17,p->last_extra,-16,10+16+128);
    }
    if (u&2)
    {
        if (u != 0xffffffff) patchstatusbar(52,183,75,193);
        digitalnumber(64,200-17,p->shield_amount,-16,10+16+128);
    }

    if (u&1024)
    {
        if (u != 0xffffffff) patchstatusbar(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+128);
        }
    }

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

            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((231-o)<<16,(200-21)<<16,65536L,0,i,0,0,10+16+permbit,0,0,xdim-1,ydim-1);
                minitext(292-30-o,190,"%",6,10+16+permbit);
                if (p->inven_icon >= 6) minitext(284-35-o,180,"AUTO",2,10+16+permbit);
            }
            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);
                else if (j != 0x80000000) minitext(284-30-o,180,"OFF",2,10+16+permbit);
            }
            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,(uint8_t )i,0,10+permbit);
            }
        }
    }
}


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

void duke_tics(short offx, short offy, short color)
{
    int32_t i;
    char  fps[512], mapname[512];
    int32_t currentFps;
    static int32_t fpsAvg = 0, savedFps = 0;
    static boolean toggle = true;
    char text[512];

    strcpy(mapname,boardfilename);
    for(i=0;i<512;i++)
        if(mapname[i]=='.')
            mapname[i]=0;

    if( mapname[0] != 0 && ud.m_level_number == 7 && ud.m_volume_number == 0 )
        sprintf(text, "%s", mapname);
    else
        //sprintf(tempbuf, "%s", level_names[ud.volume_number*11 + ud.level_number]);
        sprintf(text, "e%dl%d", ud.volume_number+1, ud.level_number+1);


    i = totalclock;

    if (i != frameval[framecnt])
    {
        currentFps = (TICRATE*AVERAGEFRAMES)/(i-frameval[framecnt]);
        fpsAvg = ((fpsAvg<<3)+(fpsAvg<<2) + (currentFps<<2))>>4;

        frameval[framecnt] = i;
    }

    framecnt = ((framecnt+1)&(AVERAGEFRAMES-1));

    // refresh screen and update visible FPS. This is to allow a refresh
    // of the screen when the screensize > 4 w/o compromising the FPS.
    if(ud.screen_size>8)
        if ((totalclock%64) < 32)
        {
            if(toggle)
            {
                vscrn();
                savedFps = fpsAvg;
            }
            toggle = false;
        }
        else
        {
            toggle = true;
        }
    else
        savedFps = fpsAvg;

    extern int rbaud_underruns, fp_calls[4];
    sprintf(fps," %d %d %d %d %d %d", savedFps, rbaud_underruns, fp_calls[0],
            fp_calls[1], fp_calls[2], fp_calls[3]);
    strcat(text, fps);

    minitext(offx,offy,text,color,2+8+16+128);
}

void coords(short snum)
{
    short x = 200, y = 0;
    char text[512];
    // x = 250 is too much on the right and
    // will make the text going out of the screen
    // if screen <= (320x200)
    // This will also *write beyond the video
    // buffer limit* and will crash the game.

    if(ud.coop != 1)
    {
        if(ud.multimode > 1 && ud.multimode < 5)
            y = 8;
        else if(ud.multimode > 4)
            y = 16;
    }

    sprintf(text,"X= %d",ps[snum].posx);
    printext256(x,y,31,-1,text,1);
    sprintf(text,"Y= %d",ps[snum].posy);
    printext256(x,y+7L,31,-1,text,1);
    sprintf(text,"Z= %d",ps[snum].posz);
    printext256(x,y+14L,31,-1,text,1);
    sprintf(text,"A= %d",ps[snum].ang);
    printext256(x,y+21L,31,-1,text,1);
    sprintf(text,"ZV= %d",ps[snum].poszv);
    printext256(x,y+28L,31,-1,text,1);
    sprintf(text,"OG= %d",ps[snum].on_ground);
    printext256(x,y+35L,31,-1,text,1);
    sprintf(text,"AM= %d",ps[snum].ammo_amount[GROW_WEAPON]);
    printext256(x,y+43L,31,-1,text,1);
    sprintf(text,"LFW= %d",ps[snum].last_full_weapon);
    printext256(x,y+50L,31,-1,text,1);
    sprintf(text,"SECTL= %d",sector[ps[snum].cursectnum].lotag);
    printext256(x,y+57L,31,-1,text,1);
    sprintf(text,"SEED= %d",randomseed);
    printext256(x,y+64L,31,-1,text,1);
    sprintf(text,"THOLD= %d",ps[snum].transporter_hold);
    printext256(x,y+64L+7,31,-1,text,1);
}

void operatefta(void)
{
    int32_t i, j, k;

    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;

        if (k > 4)
            gametext(320>>1,j,user_quote[i],0,2+8+16);
        else if (k > 2) gametext(320>>1,j,user_quote[i],0,2+8+16+1);
        else gametext(320>>1,j,user_quote[i],0,2+8+16+1+32);
        j -= 8;
    }

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

    if (ud.coop != 1 && 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)
    {
        k = quotebot;
        for(i=0;i<MAXUSERQUOTES;i++)
        {
            if (user_quote_time[i] <= 0) break;
            k -= 8;
        }
        k -= 4;
    }

    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, int mode)
{
    if( ud.fta_on == 1 || mode)
    {
        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;
        }
    }
}

void showtwoscreens(void)
{
    short i;

    if(VOLUMEONE)
    {
        setview(0,0,xdim-1,ydim-1);
        flushperms();
        ps[myconnectindex].palette = palette;
        for(i=0;i<64;i+=7) palto(0,0,0,i);
        KB_FlushKeyboardQueue();

        rotatesprite(0,0,65536L,0,3291,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);

        nextpage();
        for(i=63;i>0;i-=7) palto(0,0,0,i);

        while( !KB_KeyWaiting() ); // getpackets(); // Net already off. Trying to get packets here makes sporadic crash..

        for(i=0;i<64;i+=7)
            palto(0,0,0,i);

        KB_FlushKeyboardQueue();

        rotatesprite(0,0,65536L,0,3290,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
        nextpage();

        for(i=63;i>0;i-=7)
            palto(0,0,0,i);

        while( !KB_KeyWaiting() ); // getpackets();
    }
    else if(PLUTOPAK)
    {
        setview(0,0,xdim-1,ydim-1);
        flushperms();
        ps[myconnectindex].palette = palette;

        for(i=0;i<64;i+=7)
            palto(0,0,0,i);

        KB_FlushKeyboardQueue();

        clearview(0L);
        rotatesprite(0,0,65536L,0,TENSCREEN,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
        nextpage();

        for(i=63;i>0;i-=7)
            palto(0,0,0,i);

        totalclock = 0;

        while( !KB_KeyWaiting() );

    }
}


void gameexit(char  *msg)
{
    char  t[256];

    strncpy(t,msg,256); t[255] = 0;

    if(*t != 0) ps[myconnectindex].palette = (uint8_t  *) &palette[0];

    if(numplayers > 1)
        allowtimetocorrecterrorswhenquitting();

    if(ud.recstat == 1)
        closedemowrite();

    if(frecfilep != NULL)
    {
        fclose(frecfilep);
        frecfilep = NULL;
    }

    if(qe || cp)
        goto GOTOHERE;

    // FIX_00089: scoreboard not shown for last player who quits a DM. Only 19.7 affected. (Sarah)
    if( ud.m_recstat != 2 && ud.last_level >= 0 && playerswhenstarted > 1 && ud.coop != 1 && *t == ' ')
    {
        dobonus(1);
// CTW - MODIFICATION
//      setgamemode();
// FIX_00028: No need to call the videodriver on gameexit()
//              setgamemode(ScreenMode,ScreenWidth,ScreenHeight);
// CTW END - MODIFICATION
    }

    if(playerswhenstarted > 1)
        uninitmultiplayers();  /* deinits network transport. */

// CTW - MODIFICATION
/*  if( *t != 0 && *(t+1) != 'V' && *(t+1) != 'Y' && playonten == 0 )
    showtwoscreens();*/
    if( *t != 0 && *(t+1) != 'V' && *(t+1) != 'Y' && true)
        if(ud.showcinematics) // FIX_00029: toggle cinematics on / off
            showtwoscreens();
// CTW END - MODIFICATION

GOTOHERE:

    Shutdown();

    if(*t != 0)
    {
        setvmode(0x3);

// CTW - MODIFICATION
/*      if(playonten == 0)
        {
        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");
        printf("%s%s","\n",t);
        }*/
        if(true)
        {
            if(*t == ' ' && *(t+1) == 0) *t = 0;
            printf("\n%s\n",t);
        }
// CTW END - MODIFICATION
    }

    uninitgroupfile();

    unlink("duke3d.tmp");

    Error(EXIT_SUCCESS, "");

}




short inputloc = 0;
short strget(short x,short y,char  *t,short dalen,short c)
{
    short ch,sc;

    while(KB_KeyWaiting())
    {
        sc = 0;
        ch = KB_Getch();

        if (ch == 0)
        {

            sc = KB_Getch();
            if( sc == 104) return(1);

            continue;
        }
        else
        {
            if(ch == 8) // asc_BackSpace
            {
                if( inputloc > 0 )
                {
                    inputloc--;
                    *(t+inputloc) = 0;
                }
            }
            else
            {
                if(ch == asc_Enter || sc == 104)
                {
                    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 = toupper(ch);
                    *(t+inputloc) = ch;
                    *(t+inputloc+1) = 0;
                    inputloc++;
                }
            }
        }
    }

    if( c == 999 ) return(0);
    if( c == 998 )
    {
        char  b[41],ii;
        for(ii=0;ii<inputloc;ii++)
            b[ii] = '*';
        b[ii] = 0;
        x = gametext(x,y,b,c,2+8+16);
    }
    else x = gametext(x,y,t,c,2+8+16);
    c = 4-(sintable[(totalclock<<4)&2047]>>11);
    rotatesprite((x+8)<<16,(y+4)<<16,32768L,0,SPINNINGNUKEICON+((totalclock>>3)%7),c,0,2+8,0,0,xdim-1,ydim-1);

    return (0);
}

void typemode(void)
{
    short ch, hitstate, i, j;
    char text[512];

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

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

            strcat(recbuf,ud.user_name[myconnectindex]);
            strcat(recbuf,": ");
            strcat(recbuf,typebuf);
            j = strlen(recbuf);
            recbuf[j] = 0;
            strcat(text+1,recbuf);

            if(sendmessagecommand >= ud.multimode || movesperpacket == 4)
            {
                for(ch=connecthead;ch >= 0;ch=connectpoint2[ch])
                    if (ch != myconnectindex)
                        sendpacket(ch,(uint8_t*)tempbuf,j+1);

                adduserquote(recbuf);
                quotebot += 8;
                quotebotgoal = quotebot;
            }
            else if(sendmessagecommand >= 0)
                sendpacket(sendmessagecommand,(uint8_t*)tempbuf,j+1);

            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])
//                for(i=0;i<ud.multimode;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
                {
                    sprintf(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;

            //sprintf(buf,"PRESS 1-%ld FOR INDIVIDUAL PLAYER.",ud.multimode);
            //gametext(320>>1,j,buf,0,2+8+16); j += 8;
            //gametext(320>>1,j,"'A' OR 'ENTER' FOR ALL PLAYERS",0,2+8+16); j += 8;
            //gametext(320>>1,j,"ESC ABORTS",0,2+8+16); j += 8;

            if (ud.screen_size > 0) j = 200-45; else j = 200-8;
            gametext(320>>1,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 = strget(320>>1,j,typebuf,30,1);

        if(hitstate == 1)
        {
            KB_ClearKeyDown(sc_Enter);
            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 displayrest(int32_t smoothratio)
{
    int32_t a, i, j;

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

    pp = &ps[screenpeek];


    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();
        }
        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)
                {
                    if (screenpeek == myconnectindex && numplayers > 1)
                    {
                        cposx = omyx+mulscale16((int32_t)(myx-omyx),smoothratio);
                        cposy = omyy+mulscale16((int32_t)(myy-omyy),smoothratio);
                        cang = omyang+mulscale16((int32_t)(((myang+1024-omyang)&2047)-1024),smoothratio);
                    }
                    else
                    {
                        cposx = pp->oposx+mulscale16((int32_t)(pp->posx-pp->oposx),smoothratio);
                        cposy = pp->oposy+mulscale16((int32_t)(pp->posy-pp->oposy),smoothratio);
                        cang = pp->oang+mulscale16((int32_t)(((pp->ang+1024-pp->oang)&2047)-1024),smoothratio);
                    }
                }
                else
                {
                    cposx = pp->oposx;
                    cposy = pp->oposy;
                    cang = pp->oang;
                }
            }
            else
            {

                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 &&
            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;
        }
    }

    if(ps[myconnectindex].newowner == -1 && ud.overhead_on == 0 && ud.crosshair && ud.camerasprite == -1)
        rotatesprite((160L-(ps[myconnectindex].look_ang>>1))<<16,100L<<16,65536L,0,CROSSHAIR,0,0,2+1,windowx1,windowy1,windowx2,windowy2);

    if(ps[myconnectindex].gm&MODE_TYPE)
        typemode();
    else
    {
        CONSOLE_HandleInput();
        if( !CONSOLE_IsActive())
        {
            menus();
        }
        CONSOLE_Render();
    }

    if( ud.pause_on==1 && (ps[myconnectindex].gm&MODE_MENU) == 0 )
    {
        if (!CONSOLE_IsActive()) //Addfaz Console Pause Game line addition
        {
            menutext(160,100,0,0,"GAME PAUSED");
        }
        else
        {
            menutext(160,120,0,0,"GAME PAUSED");
        }
    }

    if(ud.coords)
        coords(screenpeek);

    // FIX_00085: Optimized Video driver. FPS increases by +20%.
    if( pp->pals_time > 0 && pp->loogcnt == 0)
    {
        palto( pp->pals[0],
               pp->pals[1],
               pp->pals[2],
               pp->pals_time|128);

        restorepalette = 1;
    }
    else if( restorepalette )
    {
        setbrightness(ud.brightness>>2,&pp->palette[0]);
        restorepalette = 0;
    }
    else if(pp->loogcnt > 0) palto(0,64,0,(pp->loogcnt>>1)+128);

}


void updatesectorz(int32_t x, int32_t y, int32_t z, short *sectnum)
{
    walltype *wal;
    int32_t i, j, cz, fz;

    getzsofslope(*sectnum,x,y,&cz,&fz);
    if ((z >= cz) && (z <= fz))
        if (inside(x,y,*sectnum) != 0) return;

    if ((*sectnum >= 0) && (*sectnum < numsectors))
    {
        wal = &wall[sector[*sectnum].wallptr];
        j = sector[*sectnum].wallnum;
        do
        {
            i = wal->nextsector;
            if (i >= 0)
            {
                getzsofslope(i,x,y,&cz,&fz);
                if ((z >= cz) && (z <= fz))
                    if (inside(x,y,(short)i) == 1)
                    { *sectnum = i; return; }
            }
            wal++; j--;
        } while (j != 0);
    }

    for(i=numsectors-1;i>=0;i--)
    {
        getzsofslope(i,x,y,&cz,&fz);
        if ((z >= cz) && (z <= fz))
            if (inside(x,y,(short)i) == 1)
            { *sectnum = i; return; }
    }

    *sectnum = -1;
}

void view(struct player_struct *pp, int32_t *vx, int32_t *vy,int32_t *vz,short *vsectnum, short ang, short horiz)
{
    spritetype *sp;
    int32_t 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;
    int32_t x,y,x1,y1,x2,y2;

    flushperms();

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

    y1 = 0; y2 = ydim;
    if( ready2send || ud.recstat == 2 )
    {
        if(ud.coop != 1)
        {
            if (ud.multimode > 1) y1 += scale(ydim,8,200);
            if (ud.multimode > 4) y1 += scale(ydim,8,200);
        }
        if (ud.screen_size >= 8) y2 = scale(ydim,200-34,200);
    }

    for(y=y1;y<y2;y+=128)
        for(x=0;x<xdim;x+=128)
            rotatesprite(x<<16,y<<16,65536L,0,dapicnum,8,0,8+16+64+128,0,y1,xdim-1,y2-1);

    // FIX_00081: Screen border in menu
    if(ud.screen_size > 8 && (ps[myconnectindex].gm & MODE_GAME || ud.recstat == 2 )) // ud.recstat == 2 => playing demo
    {
        y = 0;
        if(ud.coop != 1)
        {
            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-34,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);
    }
}


// Floor Over Floor

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

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

// If standing in sector with SE44
// then draw viewing to SE40.

// If standing in sector with SE45
// then draw viewing to SE41.

#define FOFTILE 13
#define FOFTILEX 32
#define FOFTILEY 32
int32_t tempsectorz[MAXSECTORS];
int32_t tempsectorpicnum[MAXSECTORS];
//short tempcursectnum;

static void SE40_Draw(int spnum,int32_t x,int32_t y,int32_t z,short a,short h,int32_t smoothratio)
{
    int i=0,j=0,k=0;
    int floor1=0,floor2=0,ok=0,fofmode=0;
    int32_t offx,offy;

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

    i = FOFTILE;    //Effect TILE
    if (!(gotpic[i>>3]&(1<<(i&7)))) return;
    gotpic[i>>3] &= ~(1<<(i&7));

    floor1=spnum;

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

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

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

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

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

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

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

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

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

    for(j=0;j<MAXSPRITES;j++)  // raise ceiling or floor
    {
        if(sprite[j].picnum==1 &&
           sprite[j].lotag==k+2 &&
           sprite[j].hitag==sprite[floor1].hitag
            )
        {
            if(k==40)
            {tempsectorz[sprite[j].sectnum]=sector[sprite[j].sectnum].floorz;
                sector[sprite[j].sectnum].floorz+=(((z-sector[sprite[j].sectnum].floorz)/32768)+1)*32768;
                tempsectorpicnum[sprite[j].sectnum]=sector[sprite[j].sectnum].floorpicnum;
                sector[sprite[j].sectnum].floorpicnum=13;
            }
            if(k==41)
            {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=13;
            }
        }
    }

    i=floor1;
    offx=x-sprite[i].x;
    offy=y-sprite[i].y;
    i=floor2;
    drawrooms(offx+sprite[i].x,offy+sprite[i].y,z,a,h,sprite[i].sectnum);
    animatesprites(x,y,a,smoothratio);
    drawmasks();

    for(j=0;j<MAXSPRITES;j++)  // restore ceiling or floor
    {
        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];
            }
            if(k==41)
            {sector[sprite[j].sectnum].ceilingz=tempsectorz[sprite[j].sectnum];
                sector[sprite[j].sectnum].ceilingpicnum=tempsectorpicnum[sprite[j].sectnum];
            }
        }// end if
    }// end for

} // end SE40




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

    i = headspritestat[15];
    while(i >= 0)
    {
        switch(sprite[i].lotag)
        {
//            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];
    }
}

static int32_t oyrepeat=-1;

void displayrooms(short snum,int32_t smoothratio)
{
    int32_t cposx,cposy,cposz,dst,j,fz,cz;
    short sect, cang, k, choriz;
    struct player_struct *p;
    int32_t tposx,tposy,i;
    short tang;

    p = &ps[snum];

    if(pub > 0)
    {
        if(ud.screen_size > 8) 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;

        cang = hittype[ud.camerasprite].tempang+mulscale16((int32_t)(((s->ang+1024-hittype[ud.camerasprite].tempang)&2047)-1024),smoothratio);

        se40code(s->x,s->y,s->z,cang,s->yvel,smoothratio);

        drawrooms(s->x,s->y,s->z-(4<<8),cang,s->yvel,s->sectnum);
        animatesprites(s->x,s->y,cang,smoothratio);
        drawmasks();
    }
    else
    {
        i = divscale22(1,sprite[p->i].yrepeat+28);
        if (i != oyrepeat)
        {
            oyrepeat = i;
            //printf("1: %d %d\n", oyrepeat,yxaspect);
            setaspect(oyrepeat,yxaspect);
            //printf("2: %d %d\n", oyrepeat,yxaspect);
        }

        if(screencapt)
        {
            tiles[MAXTILES-1].lock = 254;
            if (tiles[MAXTILES-1].data == NULL)
                allocache(&tiles[MAXTILES-1].data,100*160,&tiles[MAXTILES-1].lock);

            setviewtotile(MAXTILES-1,100L,160L);
        }
        else if( ( ud.screen_tilting && p->rotscrnang ) || ud.detail==0 )
        {
            if (ud.screen_tilting) tang = p->rotscrnang; else tang = 0;

            tiles[MAXTILES-2].lock = 255;
            if (tiles[MAXTILES-2].data == NULL)
                allocache(&tiles[MAXTILES-2].data,320L*320L,&tiles[MAXTILES-2].lock);
            if ((tang&1023) == 0)
                setviewtotile(MAXTILES-2,200L>>(1-ud.detail),320L>>(1-ud.detail));
            else
                setviewtotile(MAXTILES-2,320L>>(1-ud.detail),320L>>(1-ud.detail));
            if ((tang&1023) == 512)
            {     //Block off unscreen section of 90ø tilted screen
                j = ((320-60)>>(1-ud.detail));
                for(i=(60>>(1-ud.detail))-1;i>=0;i--)
                {
                    startumost[i] = 1; startumost[i+j] = 1;
                    startdmost[i] = 0; startdmost[i+j] = 0;
                }
            }

            i = (tang&511); if (i > 256) i = 512-i;
            i = sintable[i+512]*8 + sintable[i]*5L;
            setaspect(i>>1,yxaspect);
        }

        if ( (snum == myconnectindex) && (numplayers > 1) )
        {
            cposx = omyx+mulscale16((int32_t)(myx-omyx),smoothratio);
            cposy = omyy+mulscale16((int32_t)(myy-omyy),smoothratio);
            cposz = omyz+mulscale16((int32_t)(myz-omyz),smoothratio);
            cang = omyang+mulscale16((int32_t)(((myang+1024-omyang)&2047)-1024),smoothratio);
            choriz = omyhoriz+omyhorizoff+mulscale16((int32_t)(myhoriz+myhorizoff-omyhoriz-omyhorizoff),smoothratio);
            sect = mycursectnum;
        }
        else
        {
            cposx = p->oposx+mulscale16((int32_t)(p->posx-p->oposx),smoothratio);
            cposy = p->oposy+mulscale16((int32_t)(p->posy-p->oposy),smoothratio);
            cposz = p->oposz+mulscale16((int32_t)(p->posz-p->oposz),smoothratio);
            cang = p->oang+mulscale16((int32_t)(((p->ang+1024-p->oang)&2047)-1024),smoothratio);
            choriz = p->ohoriz+p->ohorizoff+mulscale16((int32_t)(p->horiz+p->horizoff-p->ohoriz-p->ohorizoff),smoothratio);
        }
        cang += p->look_ang;

        if (p->newowner >= 0)
        {
            cang = p->ang+p->look_ang;
            choriz = p->horiz+p->horizoff;
            cposx = p->posx;
            cposy = p->posy;
            cposz = p->posz;
            sect = sprite[p->newowner].sectnum;
            smoothratio = 65536L;
        }

        else if( p->over_shoulder_on == 0 )
            cposz += p->opyoff+mulscale16((int32_t)(p->pyoff-p->opyoff),smoothratio);
        else view(p,&cposx,&cposy,&cposz,&sect,cang,choriz);

        cz = hittype[p->i].ceilingz;
        fz = hittype[p->i].floorz;

        if(earthquaketime > 0 && p->on_ground == 1)
        {
            cposz += 256-(((earthquaketime)&1)<<9);
            cang += (2-((earthquaketime)&2))<<2;
        }

        if(sprite[p->i].pal == 1) cposz -= (18<<8);

        if(p->newowner >= 0)
            choriz = 100+sprite[p->newowner].shade;
        else if(p->spritebridge == 0)
        {
            if( cposz < ( p->truecz + (4<<8) ) ) cposz = cz + (4<<8);
            else if( cposz > ( p->truefz - (4<<8) ) ) cposz = fz - (4<<8);
        }

        if (sect >= 0)
        {
            getzsofslope(sect,cposx,cposy,&cz,&fz);
            if (cposz < cz+(4<<8)) cposz = cz+(4<<8);
            if (cposz > fz-(4<<8)) cposz = fz-(4<<8);
        }

        if(choriz > 299) choriz = 299;
        else if(choriz < -99) choriz = -99;

        se40code(cposx,cposy,cposz,cang,choriz,smoothratio);

        if ((gotpic[MIRROR>>3]&(1<<(MIRROR&7))) > 0)
        {
            dst = 0x7fffffff; i = 0;
            for(k=0;k<mirrorcnt;k++)
            {
                j = klabs(wall[mirrorwall[k]].x-cposx);
                j += klabs(wall[mirrorwall[k]].y-cposy);
                if (j < dst) dst = j, i = k;
            }

            if( wall[mirrorwall[i]].overpicnum == MIRROR )
            {
                preparemirror(cposx,cposy,cposz,cang,choriz,mirrorwall[i],mirrorsector[i],&tposx,&tposy,&tang);

                j = visibility;
                visibility = (j>>1) + (j>>2);

                drawrooms(tposx,tposy,cposz,tang,choriz,mirrorsector[i]+MAXSECTORS);

                display_mirror = 1;
                animatesprites(tposx,tposy,tang,smoothratio);
                display_mirror = 0;

                drawmasks();
                completemirror();   //Reverse screen x-wise in this function
                visibility = j;
            }
            gotpic[MIRROR>>3] &= ~(1<<(MIRROR&7));
        }

        drawrooms(cposx,cposy,cposz,cang,choriz,sect);
        animatesprites(cposx,cposy,cang,smoothratio);
        drawmasks();

        if(screencapt == 1)
        {
            setviewback();
            tiles[MAXTILES-1].lock = 1;
            screencapt = 0;
        }
        else if( ( ud.screen_tilting && p->rotscrnang) || ud.detail==0 )
        {
            if (ud.screen_tilting) tang = p->rotscrnang; else tang = 0;
            setviewback();
            tiles[MAXTILES-2].animFlags &= 0xff0000ff;
            i = (tang&511); if (i > 256) i = 512-i;
            i = sintable[i+512]*8 + sintable[i]*5L;
            if ((1-ud.detail) == 0) i >>= 1;
            rotatesprite(160<<16,100<<16,i,tang+512,MAXTILES-2,0,0,4+2+64,windowx1,windowy1,windowx2,windowy2);
            tiles[MAXTILES-2].lock = 199;
        }
    }

    restoreinterpolations();

    if (totalclock < lastvisinc)
    {
        if (klabs(p->visibility-ud.const_visibility) > 8)
            p->visibility += (ud.const_visibility-p->visibility)>>2;
    }
    else p->visibility = ud.const_visibility;
}





short LocateTheLocator(short n,short sn)
{
    short i;

    i = headspritestat[7];
    while(i >= 0)
    {
        if( (sn == -1 || sn == SECT) && n == SLT )
            return i;
        i = nextspritestat[i];
    }
    return -1;
}

short EGS(short whatsect,int32_t s_x,int32_t s_y,int32_t s_z,short s_pn,int8_t s_s,int8_t s_xr,int8_t s_yr,short s_a,short s_ve,int32_t s_zv,short s_ow,int8_t s_ss)
{
    short i;
    spritetype *s;

    i = insertsprite(whatsect,s_ss);

    if( i < 0 )
        gameexit(" Too many sprites spawned. This may happen (for any duke port) if you have hacked the steroids trail in the *.con files. If so, delete your *.con files to use the internal ones and try again.");

    hittype[i].bposx = s_x;
    hittype[i].bposy = s_y;
    hittype[i].bposz = s_z;

    s = &sprite[i];

    s->x = s_x;
    s->y = s_y;
    s->z = s_z;
    s->cstat = 0;
    s->picnum = s_pn;
    s->shade = s_s;
    s->xrepeat = s_xr;
    s->yrepeat = s_yr;
    s->pal = 0;

    s->ang = s_a;
    s->xvel = s_ve;
    s->zvel = s_zv;
    s->owner = s_ow;
    s->xoffset = 0;
    s->yoffset = 0;
    s->yvel = 0;
    s->clipdist = 0;
    s->pal = 0;
    s->lotag = 0;

    hittype[i].picnum = sprite[s_ow].picnum;

    hittype[i].lastvx = 0;
    hittype[i].lastvy = 0;

    hittype[i].timetosleep = 0;
    hittype[i].actorstayput = -1;
    hittype[i].extra = -1;
    hittype[i].owner = s_ow;
    hittype[i].cgg = 0;
    hittype[i].movflag = 0;
    hittype[i].tempang = 0;
    hittype[i].dispicnum = 0;
    hittype[i].floorz = hittype[s_ow].floorz;
    hittype[i].ceilingz = hittype[s_ow].ceilingz;

    T1=T3=T4=T6=0;
    if( actorscrptr[s_pn] )
    {
        s->extra = *actorscrptr[s_pn];
        T5 = *(actorscrptr[s_pn]+1);
        T2 = *(actorscrptr[s_pn]+2);
        s->hitag = *(actorscrptr[s_pn]+3);
    }
    else
    {
        T2=T5=0;
        s->extra = 0;
        s->hitag = 0;
    }

    if (show2dsector[SECT>>3]&(1<<(SECT&7))) show2dsprite[i>>3] |= (1<<(i&7));
    else show2dsprite[i>>3] &= ~(1<<(i&7));
/*
  if(s->sectnum < 0)
  {
  s->xrepeat = s->yrepeat = 0;
  changespritestat(i,5);
  }
*/
    return(i);
}

uint8_t  wallswitchcheck(short i)
{
    switch(PN)
    {
    case HANDPRINTSWITCH:
    case HANDPRINTSWITCH+1:
    case ALIENSWITCH:
    case ALIENSWITCH+1:
    case MULTISWITCH:
    case MULTISWITCH+1:
    case MULTISWITCH+2:
    case MULTISWITCH+3:
    case ACCESSSWITCH:
    case ACCESSSWITCH2:
    case PULLSWITCH:
    case PULLSWITCH+1:
    case HANDSWITCH:
    case HANDSWITCH+1:
    case SLOTDOOR:
    case SLOTDOOR+1:
    case LIGHTSWITCH:
    case LIGHTSWITCH+1:
    case SPACELIGHTSWITCH:
    case SPACELIGHTSWITCH+1:
    case SPACEDOORSWITCH:
    case SPACEDOORSWITCH+1:
    case FRANKENSTINESWITCH:
    case FRANKENSTINESWITCH+1:
    case LIGHTSWITCH2:
    case LIGHTSWITCH2+1:
    case POWERSWITCH1:
    case POWERSWITCH1+1:
    case LOCKSWITCH1:
    case LOCKSWITCH1+1:
    case POWERSWITCH2:
    case POWERSWITCH2+1:
    case DIPSWITCH:
    case DIPSWITCH+1:
    case DIPSWITCH2:
    case DIPSWITCH2+1:
    case TECHSWITCH:
    case TECHSWITCH+1:
    case DIPSWITCH3:
    case DIPSWITCH3+1:
        return 1;
    }
    return 0;
}


int32_t tempwallptr;
short spawn( short j, short pn )
{
    short i, s, startwall, endwall, sect, clostest;
    int32_t x, y, d;
    spritetype *sp;
    char text[512];

    if(j >= 0)
    {
        i = EGS(sprite[j].sectnum,sprite[j].x,sprite[j].y,sprite[j].z
                ,pn,0,0,0,0,0,0,j,0);
        hittype[i].picnum = sprite[j].picnum;
    }
    else
    {
        i = pn;

        hittype[i].picnum = PN;
        hittype[i].timetosleep = 0;
        hittype[i].extra = -1;

        hittype[i].bposx = SX;
        hittype[i].bposy = SY;
        hittype[i].bposz = SZ;

        OW = hittype[i].owner = i;
        hittype[i].cgg = 0;
        hittype[i].movflag = 0;
        hittype[i].tempang = 0;
        hittype[i].dispicnum = 0;
        hittype[i].floorz = sector[SECT].floorz;
        hittype[i].ceilingz = sector[SECT].ceilingz;

        hittype[i].lastvx = 0;
        hittype[i].lastvy = 0;
        hittype[i].actorstayput = -1;

        T1 = T2 = T3 = T4 = T5 = T6 = 0;

        if( PN != SPEAKER && PN != LETTER && PN != DUCK && PN != TARGET && PN != TRIPBOMB && PN != VIEWSCREEN && PN != VIEWSCREEN2 && (CS&48) )
            if( !(PN >= CRACK1 && PN <= CRACK4) )
            {
                if(SS == 127) return i;
                if( wallswitchcheck(i) == 1 && (CS&16) )
                {
                    if( PN != ACCESSSWITCH && PN != ACCESSSWITCH2 && sprite[i].pal)
                    {
                        if( (ud.multimode < 2) || (ud.multimode > 1 && ud.coop==1) )
                        {
                            sprite[i].xrepeat = sprite[i].yrepeat = 0;
                            sprite[i].cstat = SLT = SHT = 0;
                            return i;
                        }
                    }
                    CS |= 257;
                    if( sprite[i].pal && PN != ACCESSSWITCH && PN != ACCESSSWITCH2)
                        sprite[i].pal = 0;
                    return i;
                }

                if( SHT )
                {
                    changespritestat(i,12);
                    CS |=  257;
                    SH = impact_damage;
                    return i;
                }
            }

        s = PN;

        if( CS&1 ) CS |= 256;

        if( actorscrptr[s] )
        {
            SH = *(actorscrptr[s]);
            T5 = *(actorscrptr[s]+1);
            T2 = *(actorscrptr[s]+2);
            if( *(actorscrptr[s]+3) && SHT == 0 )
                SHT = *(actorscrptr[s]+3);
        }
        else T2 = T5 = 0;
    }

    sp = &sprite[i];
    sect = sp->sectnum;

    switch(sp->picnum)
    {
    default:

        if( actorscrptr[sp->picnum] )
        {
            if( j == -1 && sp->lotag > ud.player_skill )
            {
                sp->xrepeat=sp->yrepeat=0;
                changespritestat(i,5);
                break;
            }

            //  Init the size
            if(sp->xrepeat == 0 || sp->yrepeat == 0)
                sp->xrepeat = sp->yrepeat = 1;

            if( actortype[sp->picnum] & 3)
            {
                if( ud.monsters_off == 1 )
                {
                    sp->xrepeat=sp->yrepeat=0;
                    changespritestat(i,5);
                    break;
                }

                makeitfall(i);

                if( actortype[sp->picnum] & 2)
                    hittype[i].actorstayput = sp->sectnum;

                ps[myconnectindex].max_actors_killed++;
                sp->clipdist = 80;
                if(j >= 0)
                {
                    if(sprite[j].picnum == RESPAWN)
                        hittype[i].tempang = sprite[i].pal = sprite[j].pal;
                    changespritestat(i,1);
                }
                else changespritestat(i,2);
            }
            else
            {
                sp->clipdist = 40;
                sp->owner = i;
                changespritestat(i,1);
            }

            hittype[i].timetosleep = 0;

            if(j >= 0)
                sp->ang = sprite[j].ang;
        }
        break;
    case FOF:
        sp->xrepeat = sp->yrepeat = 0;
        changespritestat(i,5);
        break;
    case WATERSPLASH2:
        if(j >= 0)
        {
            setsprite(i,sprite[j].x,sprite[j].y,sprite[j].z);
            sp->xrepeat = sp->yrepeat = 8+(TRAND&7);
        }
        else sp->xrepeat = sp->yrepeat = 16+(TRAND&15);

        sp->shade = -16;
        sp->cstat |= 128;
        if(j >= 0)
        {
            if(sector[sprite[j].sectnum].lotag == 2)
            {
                sp->z = getceilzofslope(SECT,SX,SY)+(16<<8);
                sp->cstat |= 8;
            }
            else if( sector[sprite[j].sectnum].lotag == 1)
                sp->z = getflorzofslope(SECT,SX,SY);
        }

        if(sector[sect].floorpicnum == FLOORSLIME ||
           sector[sect].ceilingpicnum == FLOORSLIME)
            sp->pal = 7;
    case NEON1:
    case NEON2:
    case NEON3:
    case NEON4:
    case NEON5:
    case NEON6:
    case DOMELITE:
        if(sp->picnum != WATERSPLASH2)
            sp->cstat |= 257;
    case NUKEBUTTON:
        if(sp->picnum == DOMELITE)
            sp->cstat |= 257;
    case JIBS1:
    case JIBS2:
    case JIBS3:
    case JIBS4:
    case JIBS5:
    case JIBS6:
    case HEADJIB1:
    case ARMJIB1:
    case LEGJIB1:
    case LIZMANHEAD1:
    case LIZMANARM1:
    case LIZMANLEG1:
    case DUKETORSO:
    case DUKEGUN:
    case DUKELEG:
        changespritestat(i,5);
        break;
    case TONGUE:
        if(j >= 0)
            sp->ang = sprite[j].ang;
        sp->z -= 38<<8;
        sp->zvel = 256-(TRAND&511);
        sp->xvel = 64-(TRAND&127);
        changespritestat(i,4);
        break;
    case NATURALLIGHTNING:
        sp->cstat &= ~257;
        sp->cstat |= 32768;
        break;
    case TRANSPORTERSTAR:
    case TRANSPORTERBEAM:
        if(j == -1) break;
        if(sp->picnum == TRANSPORTERBEAM)
        {
            sp->xrepeat = 31;
            sp->yrepeat = 1;
            sp->z = sector[sprite[j].sectnum].floorz-(40<<8);
        }
        else
        {
            if(sprite[j].statnum == 4)
            {
                sp->xrepeat = 8;
                sp->yrepeat = 8;
            }
            else
            {
                sp->xrepeat = 48;
                sp->yrepeat = 64;
                if(sprite[j].statnum == 10 || badguy(&sprite[j]) )
                    sp->z -= (32<<8);
            }
        }

        sp->shade = -127;
        sp->cstat = 128|2;
        sp->ang = sprite[j].ang;

        sp->xvel = 128;
        changespritestat(i,5);
        ssp(i,CLIPMASK0);
        setsprite(i,sp->x,sp->y,sp->z);
        break;

    case FRAMEEFFECT1:
    case FRAMEEFFECT1_13CON:
        if(j >= 0)
        {
            sp->xrepeat = sprite[j].xrepeat;
            sp->yrepeat = sprite[j].yrepeat;
            T2 = sprite[j].picnum;
        }
        else sp->xrepeat = sp->yrepeat = 0;

        changespritestat(i,5);

        break;

    case LASERLINE:
        sp->yrepeat = 6;
        sp->xrepeat = 32;

        if(lasermode == 1)
            sp->cstat = 16 + 2;
        else if(lasermode == 0 || lasermode == 2)
            sp->cstat = 16;
        else
        {
            sp->xrepeat = 0;
            sp->yrepeat = 0;
        }

        if(j >= 0) sp->ang = hittype[j].temp_data[5]+512;
        changespritestat(i,5);
        break;

    case FORCESPHERE:
        if(j == -1 )
        {
            sp->cstat = (short) 32768;
            changespritestat(i,2);
        }
        else
        {
            sp->xrepeat = sp->yrepeat = 1;
            changespritestat(i,5);
        }
        break;

    case BLOOD:
        sp->xrepeat = sp->yrepeat = 16;
        sp->z -= (26<<8);
        if( j >= 0 && sprite[j].pal == 6 )
            sp->pal = 6;
        changespritestat(i,5);
        break;
    case BLOODPOOL:
    case PUKE:
    {
        short s1;
        s1 = sp->sectnum;

        updatesector(sp->x+108,sp->y+108,&s1);
        if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
        {
            updatesector(sp->x-108,sp->y-108,&s1);
            if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
            {
                updatesector(sp->x+108,sp->y-108,&s1);
                if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
                {
                    updatesector(sp->x-108,sp->y+108,&s1);
                    if(s1 >= 0 && sector[s1].floorz != sector[sp->sectnum].floorz)
                    { sp->xrepeat = sp->yrepeat = 0;changespritestat(i,5);break;}
                }
                else { sp->xrepeat = sp->yrepeat = 0;changespritestat(i,5);break;}
            }
            else { sp->xrepeat = sp->yrepeat = 0;changespritestat(i,5);break;}
        }
        else { sp->xrepeat = sp->yrepeat = 0;changespritestat(i,5);break;}
    }

    if( sector[SECT].lotag == 1 )
    {
        changespritestat(i,5);
        break;
    }

    if(j >= 0 && sp->picnum != PUKE)
    {
        if( sprite[j].pal == 1)
            sp->pal = 1;
        else if( sprite[j].pal != 6 && sprite[j].picnum != NUKEBARREL && sprite[j].picnum != TIRE )
        {
            if(sprite[j].picnum == FECES)
                sp->pal = 7; // Brown
            else sp->pal = 2; // Red
        }
        else sp->pal = 0;  // green

        if(sprite[j].picnum == TIRE)
            sp->shade = 127;
    }
    sp->cstat |= 32;
    case FECES:
        if( j >= 0)
            sp->xrepeat = sp->yrepeat = 1;
        changespritestat(i,5);
        break;

    case BLOODSPLAT1:
    case BLOODSPLAT2:
    case BLOODSPLAT3:
    case BLOODSPLAT4:
        sp->cstat |= 16;
        sp->xrepeat = 7+(TRAND&7);
        sp->yrepeat = 7+(TRAND&7);
        sp->z -= (16<<8);
        if(j >= 0 && sprite[j].pal == 6)
            sp->pal = 6;
        insertspriteq(i);
        changespritestat(i,5);
        break;

    case TRIPBOMB:
        if( sp->lotag > ud.player_skill )
        {
            sp->xrepeat=sp->yrepeat=0;
            changespritestat(i,5);
            break;
        }

        sp->xrepeat=4;
        sp->yrepeat=5;

        sp->owner = i;
        sp->hitag = i;

        sp->xvel = 16;
        ssp(i,CLIPMASK0);
        hittype[i].temp_data[0] = 17;
        hittype[i].temp_data[2] = 0;
        hittype[i].temp_data[5] = sp->ang;

    case SPACEMARINE:
        if(sp->picnum == SPACEMARINE)
        {
            sp->extra = 20;
            sp->cstat |= 257;
        }
        changespritestat(i,2);
        break;

    case HYDRENT:
    case PANNEL1:
    case PANNEL2:
    case SATELITE:
    case FUELPOD:
    case SOLARPANNEL:
    case ANTENNA:
    case GRATE1:
    case CHAIR1:
    case CHAIR2:
    case CHAIR3:
    case BOTTLE1:
    case BOTTLE2:
    case BOTTLE3:
    case BOTTLE4:
    case BOTTLE5:
    case BOTTLE6:
    case BOTTLE7:
    case BOTTLE8:
    case BOTTLE10:
    case BOTTLE11:
    case BOTTLE12:
    case BOTTLE13:
    case BOTTLE14:
    case BOTTLE15:
    case BOTTLE16:
    case BOTTLE17:
    case BOTTLE18:
    case BOTTLE19:
    case OCEANSPRITE1:
    case OCEANSPRITE2:
    case OCEANSPRITE3:
    case OCEANSPRITE5:
    case MONK:
    case INDY:
    case LUKE:
    case JURYGUY:
    case SCALE:
    case VACUUM:
    case FANSPRITE:
    case CACTUS:
    case CACTUSBROKE:
    case HANGLIGHT:
    case FETUS:
    case FETUSBROKE:
    case CAMERALIGHT:
    case MOVIECAMERA:
    case IVUNIT:
    case POT1:
    case POT2:
    case POT3:
    case TRIPODCAMERA:
    case SUSHIPLATE1:
    case SUSHIPLATE2:
    case SUSHIPLATE3:
    case SUSHIPLATE4:
    case SUSHIPLATE5:
    case WAITTOBESEATED:
    case VASE:
    case PIPE1:
    case PIPE2:
    case PIPE3:
    case PIPE4:
    case PIPE5:
    case PIPE6:
        sp->clipdist = 32;
        sp->cstat |= 257;
    case OCEANSPRITE4:
        changespritestat(i,0);
        break;
    case FEMMAG1:
    case FEMMAG2:
        sp->cstat &= ~257;
        changespritestat(i,0);
        break;
    case DUKETAG:
    case SIGN1:
    case SIGN2:
        if(ud.multimode < 2 && sp->pal)
        {
            sp->xrepeat = sp->yrepeat = 0;
            changespritestat(i,5);
        }
        else sp->pal = 0;
        break;
    case MASKWALL1:
    case MASKWALL2:
    case MASKWALL3:
    case MASKWALL4:
    case MASKWALL5:
    case MASKWALL6:
    case MASKWALL7:
    case MASKWALL8:
    case MASKWALL9:
    case MASKWALL10:
    case MASKWALL11:
    case MASKWALL12:
    case MASKWALL13:
    case MASKWALL14:
    case MASKWALL15:
        j = sp->cstat&60;
        sp->cstat = j|1;
        changespritestat(i,0);
        break;
    case FOOTPRINTS:
    case FOOTPRINTS2:
    case FOOTPRINTS3:
    case FOOTPRINTS4:
        if(j >= 0)
        {
            short s1;
            s1 = sp->sectnum;

            updatesector(sp->x+84,sp->y+84,&s1);
            if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
            {
                updatesector(sp->x-84,sp->y-84,&s1);
                if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
                {
                    updatesector(sp->x+84,sp->y-84,&s1);
                    if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
                    {
                        updatesector(sp->x-84,sp->y+84,&s1);
                        if(s1 >= 0 && sector[s1].floorz != sector[sp->sectnum].floorz)
                        { sp->xrepeat = sp->yrepeat = 0;changespritestat(i,5);break;}
                    }
                    else { sp->xrepeat = sp->yrepeat = 0;break;}
                }
                else { sp->xrepeat = sp->yrepeat = 0;break;}
            }
            else { sp->xrepeat = sp->yrepeat = 0;break;}

            sp->cstat = 32+((ps[sprite[j].yvel].footprintcount&1)<<2);
            sp->ang = sprite[j].ang;
        }

        sp->z = sector[sect].floorz;
        if(sector[sect].lotag != 1 && sector[sect].lotag != 2)
            sp->xrepeat = sp->yrepeat = 32;

        insertspriteq(i);
        changespritestat(i,5);
        break;

    case FEM1:
    case FEM2:
    case FEM3:
    case FEM4:
    case FEM5:
    case FEM6:
    case FEM7:
    case FEM8:
    case FEM9:
    case FEM10:
    case PODFEM1:
    case NAKED1:
    case STATUE:
    case TOUGHGAL:
        sp->yvel = sp->hitag;
        sp->hitag = -1;
        if(sp->picnum == PODFEM1) sp->extra <<= 1;
    case BLOODYPOLE:

    case QUEBALL:
    case STRIPEBALL:

        if(sp->picnum == QUEBALL || sp->picnum == STRIPEBALL)
        {
            sp->cstat = 256;
            sp->clipdist = 8;
        }
        else
        {
            sp->cstat |= 257;
            sp->clipdist = 32;
        }

        changespritestat(i,2);
        break;

    case DUKELYINGDEAD:
        if(j >= 0 && sprite[j].picnum == APLAYER)
        {
            sp->xrepeat = sprite[j].xrepeat;
            sp->yrepeat = sprite[j].yrepeat;
            sp->shade = sprite[j].shade;
            sp->pal = ps[sprite[j].yvel].palookup;
        }
    case DUKECAR:
    case HELECOPT:
//                if(sp->picnum == HELECOPT || sp->picnum == DUKECAR) sp->xvel = 1024;
        sp->cstat = 0;
        sp->extra = 1;
        sp->xvel = 292;
        sp->zvel = 360;
    case RESPAWNMARKERRED:
    case BLIMP:

        if(sp->picnum == RESPAWNMARKERRED)
        {
            sp->xrepeat = sp->yrepeat = 24;
            if(j >= 0) sp->z = hittype[j].floorz; // -(1<<4);
        }
        else
        {
            sp->cstat |= 257;
            sp->clipdist = 128;
        }
    case MIKE:
        if(sp->picnum == MIKE)
            sp->yvel = sp->hitag;
    case WEATHERWARN:
        changespritestat(i,1);
        break;

    case SPOTLITE:
        T1 = sp->x;
        T2 = sp->y;
        break;
    case BULLETHOLE:
        sp->xrepeat = sp->yrepeat = 3;
        sp->cstat = 16+(TRAND&12);
        insertspriteq(i);
    case MONEY:
    case MAIL:
    case PAPER:
        if( sp->picnum == MONEY || sp->picnum == MAIL || sp->picnum == PAPER )
        {
            hittype[i].temp_data[0] = TRAND&2047;
            sp->cstat = TRAND&12;
            sp->xrepeat = sp->yrepeat = 8;
            sp->ang = TRAND&2047;
        }
        changespritestat(i,5);
        break;

    case VIEWSCREEN:
    case VIEWSCREEN2:
        sp->owner = i;
        sp->lotag = 1;
        sp->extra = 1;
        changespritestat(i,6);
        break;

    case SHELL: //From the player
    case SHOTGUNSHELL:
        if( j >= 0 )
        {
            short snum,a;

            if(sprite[j].picnum == APLAYER)
            {
                snum = sprite[j].yvel;
                a = ps[snum].ang-(TRAND&63)+8;  //Fine tune

                T1 = TRAND&1;
                if(sp->picnum == SHOTGUNSHELL)
                    sp->z = (6<<8)+ps[snum].pyoff+ps[snum].posz-((ps[snum].horizoff+ps[snum].horiz-100)<<4);
                else sp->z = (3<<8)+ps[snum].pyoff+ps[snum].posz-((ps[snum].horizoff+ps[snum].horiz-100)<<4);
                sp->zvel = -(TRAND&255);
            }
            else
            {
                a = sp->ang;
                sp->z = sprite[j].z-PHEIGHT+(3<<8);
            }

            sp->x = sprite[j].x+(sintable[(a+512)&2047]>>7);
            sp->y = sprite[j].y+(sintable[a&2047]>>7);

            sp->shade = -8;

            sp->ang = a-512;
            sp->xvel = 20;

            //  do not try to make it 0 when ud.hideweapon Will make OOS when shooting in water
            sp->xrepeat=sp->yrepeat=4;

            changespritestat(i,5);
        }
        break;

    case RESPAWN:
        sp->extra = 66-13;
    case MUSICANDSFX:
        if( ud.multimode < 2 && sp->pal == 1)
        {
            sp->xrepeat = sp->yrepeat = 0;
            changespritestat(i,5);
            break;
        }
        sp->cstat = (short)32768;
        changespritestat(i,11);
        break;

    case EXPLOSION2:
    case EXPLOSION2BOT:
    case BURNING:
    case BURNING2:
    case SMALLSMOKE:
    case SHRINKEREXPLOSION:
    case COOLEXPLOSION1:

        if(j >= 0)
        {
            sp->ang = sprite[j].ang;
            sp->shade = -64;
            sp->cstat = 128|(TRAND&4);
        }

        if(sp->picnum == EXPLOSION2 || sp->picnum == EXPLOSION2BOT)
        {
            sp->xrepeat = 48;
            sp->yrepeat = 48;
            sp->shade = -127;
            sp->cstat |= 128;
        }
        else if(sp->picnum == SHRINKEREXPLOSION )
        {
            sp->xrepeat = 32;
            sp->yrepeat = 32;
        }
        else if( sp->picnum == SMALLSMOKE )
        {
            // 64 "money"
            sp->xrepeat = 24;
            sp->yrepeat = 24;
        }
        else if(sp->picnum == BURNING || sp->picnum == BURNING2)
        {
            sp->xrepeat = 4;
            sp->yrepeat = 4;
        }

        if(j >= 0)
        {
            x = getflorzofslope(sp->sectnum,sp->x,sp->y);
            if(sp->z > x-(12<<8) )
                sp->z = x-(12<<8);
        }

        changespritestat(i,5);

        break;

    case PLAYERONWATER:
        if(j >= 0)
        {
            sp->xrepeat = sprite[j].xrepeat;
            sp->yrepeat = sprite[j].yrepeat;
            sp->zvel = 128;
            if(sector[sp->sectnum].lotag != 2)
                sp->cstat |= 32768;
        }
        changespritestat(i,13);
        break;

    case APLAYER:
        sp->xrepeat = sp->yrepeat = 0;
        j = ud.coop;
        if(j == 2) j = 0;

        if( ud.multimode < 2 || (ud.multimode > 1 && j != sp->lotag) )
            changespritestat(i,5);
        else
            changespritestat(i,10);
        break;
    case WATERBUBBLE:
        if(j >= 0 && sprite[j].picnum == APLAYER)
            sp->z -= (16<<8);
        if( sp->picnum == WATERBUBBLE)
        {
            if( j >= 0 )
                sp->ang = sprite[j].ang;
            sp->xrepeat = sp->yrepeat = 4;
        }
        else sp->xrepeat = sp->yrepeat = 32;

        changespritestat(i,5);
        break;

    case CRANE:

        sp->cstat |= 64|257;

        sp->picnum += 2;
        sp->z = sector[sect].ceilingz+(48<<8);
        T5 = tempwallptr;

        msx[tempwallptr] = sp->x;
        msy[tempwallptr] = sp->y;
        msx[tempwallptr+2] = sp->z;

        s = headspritestat[0];
        while(s >= 0)
        {
            if( sprite[s].picnum == CRANEPOLE && SHT == (sprite[s].hitag) )
            {
                msy[tempwallptr+2] = s;

                T2 = sprite[s].sectnum;

                sprite[s].xrepeat = 48;
                sprite[s].yrepeat = 128;

                msx[tempwallptr+1] = sprite[s].x;
                msy[tempwallptr+1] = sprite[s].y;

                sprite[s].x = sp->x;
                sprite[s].y = sp->y;
                sprite[s].z = sp->z;
                sprite[s].shade = sp->shade;

                setsprite(s,sprite[s].x,sprite[s].y,sprite[s].z);
                break;
            }
            s = nextspritestat[s];
        }

        tempwallptr += 3;
        sp->owner = -1;
        sp->extra = 8;
        changespritestat(i,6);
        break;

    case WATERDRIP:
        if((j >= 0 && sprite[j].statnum == 10) || sprite[j].statnum == 1)
        {
            sp->shade = 32;
            if(sprite[j].pal != 1)
            {
                sp->pal = 2;
                sp->z -= (18<<8);
            }
            else sp->z -= (13<<8);
            sp->ang = getangle(ps[connecthead].posx-sp->x,ps[connecthead].posy-sp->y);
            sp->xvel = 48-(TRAND&31);
            ssp(i,CLIPMASK0);
        }
        else if(j == -1)
        {
            sp->z += (4<<8);
            T1 = sp->z;
            T2 = TRAND&127;
        }
    case TRASH:

        if(sp->picnum != WATERDRIP)
            sp->ang = TRAND&2047;

    case WATERDRIPSPLASH:

        sp->xrepeat = 24;
        sp->yrepeat = 24;


        changespritestat(i,6);
        break;

    case PLUG:
        sp->lotag = 9999;
        changespritestat(i,6);
        break;
    case TOUCHPLATE:
        T3 = sector[sect].floorz;
        if(sector[sect].lotag != 1 && sector[sect].lotag != 2)
            sector[sect].floorz = sp->z;
        if(sp->pal && ud.multimode > 1)
        {
            sp->xrepeat=sp->yrepeat=0;
            changespritestat(i,5);
            break;
        }
    case WATERBUBBLEMAKER:
        sp->cstat |= 32768;
        changespritestat(i,6);
        break;
    case BOLT1:
    case BOLT1+1:
    case BOLT1+2:
    case BOLT1+3:
    case SIDEBOLT1:
    case SIDEBOLT1+1:
    case SIDEBOLT1+2:
    case SIDEBOLT1+3:
        T1 = sp->xrepeat;
        T2 = sp->yrepeat;
    case MASTERSWITCH:
        if(sp->picnum == MASTERSWITCH)
            sp->cstat |= 32768;
        sp->yvel = 0;
        changespritestat(i,6);
        break;
    case TARGET:
    case DUCK:
    case LETTER:
        sp->extra = 1;
        sp->cstat |= 257;
        changespritestat(i,1);
        break;
    case OCTABRAINSTAYPUT:
    case LIZTROOPSTAYPUT:
    case PIGCOPSTAYPUT:
    case LIZMANSTAYPUT:
    case BOSS1STAYPUT:
    case PIGCOPDIVE:
    case COMMANDERSTAYPUT:
    case BOSS4STAYPUT:
        hittype[i].actorstayput = sp->sectnum;
    case BOSS1:
    case BOSS2:
    case BOSS3:
    case BOSS4:
    case ROTATEGUN:
    case GREENSLIME:
        if(sp->picnum == GREENSLIME)
            sp->extra = 1;
    case DRONE:
    case LIZTROOPONTOILET:
    case LIZTROOPJUSTSIT:
    case LIZTROOPSHOOT:
    case LIZTROOPJETPACK:
    case LIZTROOPDUCKING:
    case LIZTROOPRUNNING:
    case LIZTROOP:
    case OCTABRAIN:
    case COMMANDER:
    case PIGCOP:
    case LIZMAN:
    case LIZMANSPITTING:
    case LIZMANFEEDING:
    case LIZMANJUMP:
    case ORGANTIC:
    case RAT:
    case SHARK:

        if(sp->pal == 0)
        {
            switch(sp->picnum)
            {
            case LIZTROOPONTOILET:
            case LIZTROOPSHOOT:
            case LIZTROOPJETPACK:
            case LIZTROOPDUCKING:
            case LIZTROOPRUNNING:
            case LIZTROOPSTAYPUT:
            case LIZTROOPJUSTSIT:
            case LIZTROOP:
                sp->pal = 22;
                break;
            }
        }

        if( sp->picnum == BOSS4STAYPUT || sp->picnum == BOSS1 || sp->picnum == BOSS2 || sp->picnum == BOSS1STAYPUT || sp->picnum == BOSS3 || sp->picnum == BOSS4 )
        {
            if(j >= 0 && sprite[j].picnum == RESPAWN)
                sp->pal = sprite[j].pal;
            if(sp->pal)
            {
                sp->clipdist = 80;
                sp->xrepeat = 40;
                sp->yrepeat = 40;
            }
            else
            {
                sp->xrepeat = 80;
                sp->yrepeat = 80;
                sp->clipdist = 164;
            }
        }
        else
        {
            if(sp->picnum != SHARK)
            {
                sp->xrepeat = 40;
                sp->yrepeat = 40;
                sp->clipdist = 80;
            }
            else
            {
                sp->xrepeat = 60;
                sp->yrepeat = 60;
                sp->clipdist = 40;
            }
        }

        if(j >= 0) sp->lotag = 0;

        if( ( sp->lotag > ud.player_skill ) || ud.monsters_off == 1 )
        {
            sp->xrepeat=sp->yrepeat=0;
            changespritestat(i,5);
            break;
        }
        else
        {
            makeitfall(i);

            if(sp->picnum == RAT)
            {
                sp->ang = TRAND&2047;
                sp->xrepeat = sp->yrepeat = 48;
                sp->cstat = 0;
            }
            else
            {
                sp->cstat |= 257;

                if(sp->picnum != SHARK)
                    ps[myconnectindex].max_actors_killed++;
            }

            if(sp->picnum == ORGANTIC) sp->cstat |= 128;

            if(j >= 0)
            {
                hittype[i].timetosleep = 0;
                check_fta_sounds(i);
                changespritestat(i,1);
            }
            else changespritestat(i,2);
        }

        if(sp->picnum == ROTATEGUN)
            sp->zvel = 0;

        break;

    case LOCATORS:
        sp->cstat |= 32768;
        changespritestat(i,7);
        break;

    case ACTIVATORLOCKED:
    case ACTIVATOR:
        sp->cstat = (short) 32768;
        if(sp->picnum == ACTIVATORLOCKED)
            sector[sp->sectnum].lotag |= 16384;
        changespritestat(i,8);
        break;

    case DOORSHOCK:
        sp->cstat |= 1+256;
        sp->shade = -12;
        changespritestat(i,6);
        break;

    case OOZ:
    case OOZ2:
        sp->shade = -12;

        if(j >= 0)
        {
            if( sprite[j].picnum == NUKEBARREL )
                sp->pal = 8;
            insertspriteq(i);
        }

        changespritestat(i,1);

        getglobalz(i);

        j = (hittype[i].floorz-hittype[i].ceilingz)>>9;

        sp->yrepeat = j;
        sp->xrepeat = 25-(j>>1);
        sp->cstat |= (TRAND&4);

        break;

    case HEAVYHBOMB:
        if(j >= 0)
            sp->owner = j;
        else sp->owner = i;
        sp->xrepeat = sp->yrepeat = 9;
        sp->yvel = 4;
    case REACTOR2:
    case REACTOR:
    case RECON:

        if(sp->picnum == RECON)
        {
            if( sp->lotag > ud.player_skill )
            {
                sp->xrepeat = sp->yrepeat = 0;
                changespritestat(i,5);
                return i;
            }
            ps[myconnectindex].max_actors_killed++;
            hittype[i].temp_data[5] = 0;
            if(ud.monsters_off == 1)
            {
                sp->xrepeat = sp->yrepeat = 0;
                changespritestat(i,5);
                break;
            }
            sp->extra = 130;
        }

        if(sp->picnum == REACTOR || sp->picnum == REACTOR2)
            sp->extra = impact_damage;

        CS |= 257; // Make it hitable

        if( ud.multimode < 2 && sp->pal != 0)
        {
            sp->xrepeat = sp->yrepeat = 0;
            changespritestat(i,5);
            break;
        }
        sp->pal = 0;
        SS = -17;

        changespritestat(i,2);
        break;

    case ATOMICHEALTH:
    case STEROIDS:
    case HEATSENSOR:
    case SHIELD:
    case AIRTANK:
    case TRIPBOMBSPRITE:
    case JETPACK:
    case HOLODUKE:

    case FIRSTGUNSPRITE:
    case CHAINGUNSPRITE:
    case SHOTGUNSPRITE:
    case RPGSPRITE:
    case SHRINKERSPRITE:
    case FREEZESPRITE:
    case DEVISTATORSPRITE:

    case SHOTGUNAMMO:
    case FREEZEAMMO:
    case HBOMBAMMO:
    case CRYSTALAMMO:
    case GROWAMMO:
    case BATTERYAMMO:
    case DEVISTATORAMMO:
    case RPGAMMO:
    case BOOTS:
    case AMMO:
    case AMMOLOTS:
    case COLA:
    case FIRSTAID:
    case SIXPAK:
        if(j >= 0)
        {
            sp->lotag = 0;
            sp->z -= (32<<8);
            sp->zvel = -1024;
            ssp(i,CLIPMASK0);
            sp->cstat = TRAND&4;
        }
        else
        {
            sp->owner = i;
            sp->cstat = 0;
        }

        if( ( ud.multimode < 2 && sp->pal != 0) || (sp->lotag > ud.player_skill) )
        {
            sp->xrepeat = sp->yrepeat = 0;
            changespritestat(i,5);
            break;
        }

        sp->pal = 0;

    case ACCESSCARD:

        if(sp->picnum == ATOMICHEALTH)
            sp->cstat |= 128;

        if(ud.multimode > 1 && ud.coop != 1 && sp->picnum == ACCESSCARD)
        {
            sp->xrepeat = sp->yrepeat = 0;
            changespritestat(i,5);
            break;
        }
        else
        {
            if(sp->picnum == AMMO)
                sp->xrepeat = sp->yrepeat = 16;
            else sp->xrepeat = sp->yrepeat = 32;
        }

        sp->shade = -17;

        if(j >= 0) changespritestat(i,1);
        else
        {
            changespritestat(i,2);
            makeitfall(i);
        }
        break;

    case WATERFOUNTAIN:
        SLT = 1;

    case TREE1:
    case TREE2:
    case TIRE:
    case CONE:
    case BOX:
        CS = 257; // Make it hitable
        sprite[i].extra = 1;
        changespritestat(i,6);
        break;

    case FLOORFLAME:
        sp->shade = -127;
        changespritestat(i,6);
        break;

    case BOUNCEMINE:
        sp->owner = i;
        sp->cstat |= 1+256; //Make it hitable
        sp->xrepeat = sp->yrepeat = 24;
        sp->shade = -127;
        sp->extra = impact_damage<<2;
        changespritestat(i,2);
        break;

    case CAMERA1:
    case CAMERA1+1:
    case CAMERA1+2:
    case CAMERA1+3:
    case CAMERA1+4:
    case CAMERAPOLE:
        sp->extra = 1;

        if(camerashitable) sp->cstat = 257;
        else sp->cstat = 0;

    case GENERICPOLE:

        if( ud.multimode < 2 && sp->pal != 0 )
        {
            sp->xrepeat = sp->yrepeat = 0;
            changespritestat(i,5);
            break;
        }
        else sp->pal = 0;
        if(sp->picnum == CAMERAPOLE || sp->picnum == GENERICPOLE) break;
        sp->picnum = CAMERA1;
        changespritestat(i,1);
        break;
    case STEAM:
        if(j >= 0)
        {
            sp->ang = sprite[j].ang;
            sp->cstat = 16+128+2;
            sp->xrepeat=sp->yrepeat=1;
            sp->xvel = -8;
            ssp(i,CLIPMASK0);
        }
    case CEILINGSTEAM:
        changespritestat(i,6);
        break;

    case SECTOREFFECTOR:
        sp->yvel = sector[sect].extra;
        sp->cstat |= 32768;
        sp->xrepeat = sp->yrepeat = 0;

        switch(sp->lotag)
        {
        case 28:
            T6 = 65;// Delay for lightning
            break;
        case 7: // Transporters!!!!
        case 23:// XPTR END
            if(sp->lotag != 23)
            {
                for(j=0;j<MAXSPRITES;j++)
                    if(sprite[j].statnum < MAXSTATUS && sprite[j].picnum == SECTOREFFECTOR && ( sprite[j].lotag == 7 || sprite[j].lotag == 23 ) && i != j && sprite[j].hitag == SHT)
                    {
                        OW = j;
                        break;
                    }
            }
            else OW = i;

            T5 = sector[sect].floorz == SZ;
            sp->cstat = 0;
            changespritestat(i,9);
            return i;
        case 1:
            sp->owner = -1;
            T1 = 1;
            break;
        case 18:

            if(sp->ang == 512)
            {
                T2 = sector[sect].ceilingz;
                if(sp->pal)
                    sector[sect].ceilingz = sp->z;
            }
            else
            {
                T2 = sector[sect].floorz;
                if(sp->pal)
                    sector[sect].floorz = sp->z;
            }

            sp->hitag <<= 2;
            break;

        case 19:
            sp->owner = -1;
            break;
        case 25: // Pistons
            T4 = sector[sect].ceilingz;
            T5 = 1;
            sector[sect].ceilingz = sp->z;
            setinterpolation(&sector[sect].ceilingz);
            break;
        case 35:
            sector[sect].ceilingz = sp->z;
            break;
        case 27:
            if(ud.recstat == 1)
            {
                sp->xrepeat=sp->yrepeat=64;
                sp->cstat &= 32767;
            }
            break;
        case 12:

            T2 = sector[sect].floorshade;
            T3 = sector[sect].ceilingshade;
            break;

        case 13:

            T1 = sector[sect].ceilingz;
            T2 = sector[sect].floorz;

            if( klabs(T1-sp->z) < klabs(T2-sp->z) )
                sp->owner = 1;
            else sp->owner = 0;

            if(sp->ang == 512)
            {
                if(sp->owner)
                    sector[sect].ceilingz = sp->z;
                else
                    sector[sect].floorz = sp->z;
            }
            else
                sector[sect].ceilingz = sector[sect].floorz = sp->z;

            if( sector[sect].ceilingstat&1 )
            {
                sector[sect].ceilingstat ^= 1;
                T4 = 1;

                if(!sp->owner && sp->ang==512)
                {
                    sector[sect].ceilingstat ^= 1;
                    T4 = 0;
                }

                sector[sect].ceilingshade =
                    sector[sect].floorshade;

                if(sp->ang==512)
                {
                    startwall = sector[sect].wallptr;
                    endwall = startwall+sector[sect].wallnum;
                    for(j=startwall;j<endwall;j++)
                    {
                        x = wall[j].nextsector;
                        if(x >= 0)
                            if( !(sector[x].ceilingstat&1) )
                            {
                                sector[sect].ceilingpicnum =
                                    sector[x].ceilingpicnum;
                                sector[sect].ceilingshade =
                                    sector[x].ceilingshade;
                                break; //Leave earily
                            }
                    }
                }
            }

            break;

        case 17:

            T3 = sector[sect].floorz; //Stopping loc

            j = nextsectorneighborz(sect,sector[sect].floorz,-1,-1);
            T4 = sector[j].ceilingz;

            j = nextsectorneighborz(sect,sector[sect].ceilingz,1,1);
            T5 = sector[j].floorz;

            if(numplayers < 2)
            {
                setinterpolation(&sector[sect].floorz);
                setinterpolation(&sector[sect].ceilingz);
            }

            break;

        case 24:
            sp->yvel <<= 1;
        case 36:
            break;

        case 20:
        {
            int32_t q;

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

            //find the two most clostest wall x's and y's
            q = 0x7fffffff;

            for(s=startwall;s<endwall;s++)
            {
                x = wall[s].x;
                y = wall[s].y;

                d = FindDistance2D(sp->x-x,sp->y-y);
                if( d < q )
                {
                    q = d;
                    clostest = s;
                }
            }

            T2 = clostest;

            q = 0x7fffffff;

            for(s=startwall;s<endwall;s++)
            {
                x = wall[s].x;
                y = wall[s].y;

                d = FindDistance2D(sp->x-x,sp->y-y);
                if(d < q && s != T2)
                {
                    q = d;
                    clostest = s;
                }
            }

            T3 = clostest;
        }

        break;

        case 3:

            T4=sector[sect].floorshade;

            sector[sect].floorshade = sp->shade;
            sector[sect].ceilingshade = sp->shade;

            sp->owner = sector[sect].ceilingpal<<8;
            sp->owner |= sector[sect].floorpal;

            //fix all the walls;

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

            for(s=startwall;s<endwall;s++)
            {
                if(!(wall[s].hitag&1))
                    wall[s].shade=sp->shade;
                if( (wall[s].cstat&2) && wall[s].nextwall >= 0)
                    wall[wall[s].nextwall].shade = sp->shade;
            }
            break;

        case 31:
            T2 = sector[sect].floorz;
            //    T3 = sp->hitag;
            if(sp->ang != 1536) sector[sect].floorz = sp->z;

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

            for(s=startwall;s<endwall;s++)
                if(wall[s].hitag == 0) wall[s].hitag = 9999;

            setinterpolation(&sector[sect].floorz);

            break;
        case 32:
            T2 = sector[sect].ceilingz;
            T3 = sp->hitag;
            if(sp->ang != 1536) sector[sect].ceilingz = sp->z;

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

            for(s=startwall;s<endwall;s++)
                if(wall[s].hitag == 0) wall[s].hitag = 9999;

            setinterpolation(&sector[sect].ceilingz);

            break;

        case 4: //Flashing lights

            T3 = sector[sect].floorshade;

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

            sp->owner = sector[sect].ceilingpal<<8;
            sp->owner |= sector[sect].floorpal;

            for(s=startwall;s<endwall;s++)
                if(wall[s].shade > T4)
                    T4 = wall[s].shade;

            break;

        case 9:
            if( sector[sect].lotag &&
                labs(sector[sect].ceilingz-sp->z) > 1024)
                sector[sect].lotag |= 32768; //If its open
        case 8:
            //First, get the ceiling-floor shade

            T1 = sector[sect].floorshade;
            T2 = sector[sect].ceilingshade;

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

            for(s=startwall;s<endwall;s++)
                if(wall[s].shade > T3)
                    T3 = wall[s].shade;

            T4 = 1; //Take Out;

            break;

        case 11://Pivitor rotater
            if(sp->ang>1024) T4 = 2;
            else T4 = -2;
        case 0:
        case 2://Earthquakemakers
        case 5://Boss Creature
        case 6://Subway
        case 14://Caboos
        case 15://Subwaytype sliding door
        case 16://That rotating blocker reactor thing
        case 26://ESCELATOR
        case 30://No rotational subways

            if(sp->lotag == 0)
            {
                if( sector[sect].lotag == 30 )
                {
                    if(sp->pal) sprite[i].clipdist = 1;
                    else sprite[i].clipdist = 0;
                    T4 = sector[sect].floorz;
                    sector[sect].hitag = i;
                }

                for(j = 0;j < MAXSPRITES;j++)
                {
                    if( sprite[j].statnum < MAXSTATUS )
                        if( sprite[j].picnum == SECTOREFFECTOR &&
                            sprite[j].lotag == 1 &&
                            sprite[j].hitag == sp->hitag)
                        {
                            if( sp->ang == 512 )
                            {
                                sp->x = sprite[j].x;
                                sp->y = sprite[j].y;
                            }
                            break;
                        }
                }
                if(j == MAXSPRITES)
                {
                    sprintf(text,"Found lonely Sector Effector (lotag 0) at (%d,%d)\n",sp->x,sp->y);
                    gameexit(text);
                }
                sp->owner = j;
            }

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

            T2 = tempwallptr;
            for(s=startwall;s<endwall;s++)
            {
                msx[tempwallptr] = wall[s].x-sp->x;
                msy[tempwallptr] = wall[s].y-sp->y;
                tempwallptr++;
                if(tempwallptr > 2047)
                {
                    sprintf(text,"Too many moving sectors at (%d,%d).\n",wall[s].x,wall[s].y);
                    gameexit(text);
                }
            }
            if( sp->lotag == 30 || sp->lotag == 6 || sp->lotag == 14 || sp->lotag == 5 )
            {

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

                if(sector[sect].hitag == -1)
                    sp->extra = 0;
                else sp->extra = 1;

                sector[sect].hitag = i;

                j = 0;

                for(s=startwall;s<endwall;s++)
                {
                    if( wall[ s ].nextsector >= 0 &&
                        sector[ wall[ s ].nextsector].hitag == 0 &&
                        sector[ wall[ s ].nextsector].lotag < 3 )
                    {
                        s = wall[s].nextsector;
                        j = 1;
                        break;
                    }
                }

                if(j == 0)
                {
                    sprintf(text,"Subway found no zero'd sectors with locators\nat (%d,%d).\n",sp->x,sp->y);
                    gameexit(text);
                }

                sp->owner = -1;
                T1 = s;

                if(sp->lotag != 30)
                    T4 = sp->hitag;
            }

            else if(sp->lotag == 16)
                T4 = sector[sect].ceilingz;

            else if( sp->lotag == 26 )
            {
                T4 = sp->x;
                T5 = sp->y;
                if(sp->shade==sector[sect].floorshade) //UP
                    sp->zvel = -256;
                else
                    sp->zvel = 256;

                sp->shade = 0;
            }
            else if( sp->lotag == 2)
            {
                T6 = sector[sp->sectnum].floorheinum;
                sector[sp->sectnum].floorheinum = 0;
            }
        }

        switch(sp->lotag)
        {
        case 6:
        case 14:
            j = callsound(sect,i);
            if(j == -1) j = SUBWAY;
            hittype[i].lastvx = j;
        case 30:
            if(numplayers > 1) break;
        case 0:
        case 1:
        case 5:
        case 11:
        case 15:
        case 16:
        case 26:
            setsectinterpolate(i);
            break;
        }

        switch(sprite[i].lotag)
        {
        case 40:
        case 41:
        case 43:
        case 44:
        case 45:
            changespritestat(i,15);
            break;
        default:
            changespritestat(i,3);
            break;
        }

        break;


    case SEENINE:
    case OOZFILTER:

        sp->shade = -16;
        if(sp->xrepeat <= 8)
        {
            sp->cstat = (short)32768;
            sp->xrepeat=sp->yrepeat=0;
        }
        else sp->cstat = 1+256;
        sp->extra = impact_damage<<2;
        sp->owner = i;

        changespritestat(i,6);
        break;

    case CRACK1:
    case CRACK2:
    case CRACK3:
    case CRACK4:
    case FIREEXT:
        if(sp->picnum == FIREEXT)
        {
            sp->cstat = 257;
            sp->extra = impact_damage<<2;
        }
        else
        {
            sp->cstat |= 17;
            sp->extra = 1;
        }

        if( ud.multimode < 2 && sp->pal != 0)
        {
            sp->xrepeat = sp->yrepeat = 0;
            changespritestat(i,5);
            break;
        }

        sp->pal = 0;
        sp->owner = i;
        changespritestat(i,6);
        sp->xvel = 8;
        ssp(i,CLIPMASK0);
        break;

    case TOILET:
    case STALL:
        sp->lotag = 1;
        sp->cstat |= 257;
        sp->clipdist = 8;
        sp->owner = i;
        break;
    case CANWITHSOMETHING:
    case CANWITHSOMETHING2:
    case CANWITHSOMETHING3:
    case CANWITHSOMETHING4:
    case RUBBERCAN:
        sp->extra = 0;
    case EXPLODINGBARREL:
    case HORSEONSIDE:
    case FIREBARREL:
    case NUKEBARREL:
    case FIREVASE:
    case NUKEBARRELDENTED:
    case NUKEBARRELLEAKED:
    case WOODENHORSE:

        if(j >= 0)
            sp->xrepeat = sp->yrepeat = 32;
        sp->clipdist = 72;
        makeitfall(i);
        if(j >= 0)
            sp->owner = j;
        else sp->owner = i;
    case EGG:
        if( ud.monsters_off == 1 && sp->picnum == EGG )
        {
            sp->xrepeat = sp->yrepeat = 0;
            changespritestat(i,5);
        }
        else
        {
            if(sp->picnum == EGG)
                sp->clipdist = 24;
            sp->cstat = 257|(TRAND&4);
            changespritestat(i,2);
        }
        break;
    case TOILETWATER:
        sp->shade = -16;
        changespritestat(i,6);
        break;
    }
    return i;
}


void animatesprites(int32_t x,int32_t y,short a,int32_t smoothratio)
{
    short i, j, k, p, sect;
    int32_t l, t1,t3,t4;
    spritetype *s,*t;

    for(j=0;j < spritesortcnt; j++)
    {
        t = &tsprite[j];
        i = t->owner;
        s = &sprite[t->owner];

        switch(t->picnum)
        {
        case BLOODPOOL:
        case PUKE:
        case FOOTPRINTS:
        case FOOTPRINTS2:
        case FOOTPRINTS3:
        case FOOTPRINTS4:
            if(t->shade == 127) continue;
            break;
        case RESPAWNMARKERRED:
        case RESPAWNMARKERYELLOW:
        case RESPAWNMARKERGREEN:
            if(ud.marker == 0)
                t->xrepeat = t->yrepeat = 0;
            continue;
        case CHAIR3:

            k = (((t->ang+3072+128-a)&2047)>>8)&7;
            if(k>4)
            {
                k = 8-k;
                t->cstat |= 4;
            }
            else t->cstat &= ~4;
            t->picnum = s->picnum+k;
            break;
        case BLOODSPLAT1:
        case BLOODSPLAT2:
        case BLOODSPLAT3:
        case BLOODSPLAT4:
            if(ud.lockout) t->xrepeat = t->yrepeat = 0;
            else if(t->pal == 6)
            {
                t->shade = -127;
                continue;
            }
        case BULLETHOLE:
        case CRACK1:
        case CRACK2:
        case CRACK3:
        case CRACK4:
            t->shade = 16;
            continue;
        case NEON1:
        case NEON2:
        case NEON3:
        case NEON4:
        case NEON5:
        case NEON6:
            continue;
        case GREENSLIME:
        case GREENSLIME+1:
        case GREENSLIME+2:
        case GREENSLIME+3:
        case GREENSLIME+4:
        case GREENSLIME+5:
        case GREENSLIME+6:
        case GREENSLIME+7:
            break;
        default:
            if( ( (t->cstat&16) ) || ( badguy(t) && t->extra > 0) || t->statnum == 10)
                continue;
        }

        if (sector[t->sectnum].ceilingstat&1)
            l = sector[t->sectnum].ceilingshade;
        else
            l = sector[t->sectnum].floorshade;

        if(l < -127) l = -127;
        if(l > 128) l =  127;
        t->shade = l;
    }


    for(j=0;j < spritesortcnt; j++ )  //Between drawrooms() and drawmasks()
    {                             //is the perfect time to animate sprites
        t = &tsprite[j];
        i = t->owner;
        s = &sprite[i];

        switch(s->picnum)
        {
        case SECTOREFFECTOR:
            if(t->lotag == 27 && ud.recstat == 1)
            {
                t->picnum = 11+((totalclock>>3)&1);
                t->cstat |= 128;
            }
            else
                t->xrepeat = t->yrepeat = 0;
            break;
        case NATURALLIGHTNING:
            t->shade = -127;
            break;
        case FEM1:
        case FEM2:
        case FEM3:
        case FEM4:
        case FEM5:
        case FEM6:
        case FEM7:
        case FEM8:
        case FEM9:
        case FEM10:
        case MAN:
        case MAN2:
        case WOMAN:
        case NAKED1:
        case PODFEM1:
        case FEMMAG1:
        case FEMMAG2:
        case FEMPIC1:
        case FEMPIC2:
        case FEMPIC3:
        case FEMPIC4:
        case FEMPIC5:
        case FEMPIC6:
        case FEMPIC7:
        case BLOODYPOLE:
        case FEM6PAD:
        case STATUE:
        case STATUEFLASH:
        case OOZ:
        case OOZ2:
        case WALLBLOOD1:
        case WALLBLOOD2:
        case WALLBLOOD3:
        case WALLBLOOD4:
        case WALLBLOOD5:
        case WALLBLOOD7:
        case WALLBLOOD8:
        case SUSHIPLATE1:
        case SUSHIPLATE2:
        case SUSHIPLATE3:
        case SUSHIPLATE4:
        case FETUS:
        case FETUSJIB:
        case FETUSBROKE:
        case HOTMEAT:
        case FOODOBJECT16:
        case DOLPHIN1:
        case DOLPHIN2:
        case TOUGHGAL:
        case TAMPON:
        case XXXSTACY:
        case 4946:
        case 4947:
        case 693:
        case 2254:
        case 4560:
        case 4561:
        case 4562:
        case 4498:
        case 4957:
            if(ud.lockout)
            {
                t->xrepeat = t->yrepeat = 0;
                continue;
            }
        }

        if( t->statnum == 99 ) continue;
        if( s->statnum != 1 && s->picnum == APLAYER && ps[s->yvel].newowner == -1 && s->owner >= 0 )
        {
            t->x -= mulscale16(65536-smoothratio,ps[s->yvel].posx-ps[s->yvel].oposx);
            t->y -= mulscale16(65536-smoothratio,ps[s->yvel].posy-ps[s->yvel].oposy);
            t->z = ps[s->yvel].oposz + mulscale16(smoothratio,ps[s->yvel].posz-ps[s->yvel].oposz);
            t->z += (40<<8);
        }
        else if( ( s->statnum == 0 && s->picnum != CRANEPOLE) || s->statnum == 10 || s->statnum == 6 || s->statnum == 4 || s->statnum == 5 || s->statnum == 1 )
        {
            t->x -= mulscale16(65536-smoothratio,s->x-hittype[i].bposx);
            t->y -= mulscale16(65536-smoothratio,s->y-hittype[i].bposy);
            t->z -= mulscale16(65536-smoothratio,s->z-hittype[i].bposz);
        }

        sect = s->sectnum;
        t1 = T2;t3 = T4;t4 = T5;

        switch(s->picnum)
        {
        case DUKELYINGDEAD:
            t->z += (24<<8);
            break;
        case BLOODPOOL:
        case FOOTPRINTS:
        case FOOTPRINTS2:
        case FOOTPRINTS3:
        case FOOTPRINTS4:
            if(t->pal == 6)
                t->shade = -127;
        case PUKE:
        case MONEY:
        case MONEY+1:
        case MAIL:
        case MAIL+1:
        case PAPER:
        case PAPER+1:
            if(ud.lockout && s->pal == 2)
            {
                t->xrepeat = t->yrepeat = 0;
                continue;
            }
            break;
        case TRIPBOMB:
            continue;
        case FORCESPHERE:
            if(t->statnum == 5)
            {
                short sqa,sqb;

                sqa =
                    getangle(
                        sprite[s->owner].x-ps[screenpeek].posx,
                        sprite[s->owner].y-ps[screenpeek].posy);
                sqb =
                    getangle(
                        sprite[s->owner].x-t->x,
                        sprite[s->owner].y-t->y);

                if( klabs(getincangle(sqa,sqb)) > 512 )
                    if( ldist(&sprite[s->owner],t) < ldist(&sprite[ps[screenpeek].i],&sprite[s->owner]) )
                        t->xrepeat = t->yrepeat = 0;
            }
            continue;
        case BURNING:
        case BURNING2:
            if( sprite[s->owner].statnum == 10 )
            {
                if( display_mirror == 0 && sprite[s->owner].yvel == screenpeek && ps[sprite[s->owner].yvel].over_shoulder_on == 0 )
                    t->xrepeat = 0;
                else
                {
                    t->ang = getangle(x-t->x,y-t->y);
                    t->x = sprite[s->owner].x;
                    t->y = sprite[s->owner].y;
                    t->x += sintable[(t->ang+512)&2047]>>10;
                    t->y += sintable[t->ang&2047]>>10;
                }
            }
            break;

        case ATOMICHEALTH:
            t->z -= (4<<8);
            break;
        case CRYSTALAMMO:
            t->shade = (sintable[(totalclock<<4)&2047]>>10);
            continue;
        case VIEWSCREEN:
        case VIEWSCREEN2:
            if(camsprite >= 0 && hittype[OW].temp_data[0] == 1)
            {
                t->picnum = STATIC;
                t->cstat |= (rand()&12);
                t->xrepeat += 8;
                t->yrepeat += 8;
            }
            break;

        case SHRINKSPARK:
            t->picnum = SHRINKSPARK+( (totalclock>>4)&3 );
            break;
        case GROWSPARK:
            t->picnum = GROWSPARK+( (totalclock>>4)&3 );
            break;
        case RPG:
            k = getangle(s->x-x,s->y-y);
            k = (((s->ang+3072+128-k)&2047)/170);
            if(k > 6)
            {
                k = 12-k;
                t->cstat |= 4;
            }
            else t->cstat &= ~4;
            t->picnum = RPG+k;
            break;

        case RECON:

            k = getangle(s->x-x,s->y-y);
            if( T1 < 4 )
                k = (((s->ang+3072+128-k)&2047)/170);
            else k = (((s->ang+3072+128-k)&2047)/170);

            if(k>6)
            {
                k = 12-k;
                t->cstat |= 4;
            }
            else t->cstat &= ~4;

            if( klabs(t3) > 64 ) k += 7;
            t->picnum = RECON+k;

            break;

        case APLAYER:

            p = s->yvel;

            if(t->pal == 1) t->z -= (18<<8);

            if(ps[p].over_shoulder_on > 0 && ps[p].newowner < 0 )
            {
                t->cstat |= 2;
                if ( screenpeek == myconnectindex && numplayers >= 2 )
                {
                    t->x = omyx+mulscale16((int32_t)(myx-omyx),smoothratio);
                    t->y = omyy+mulscale16((int32_t)(myy-omyy),smoothratio);
                    t->z = omyz+mulscale16((int32_t)(myz-omyz),smoothratio)+(40<<8);
                    t->ang = omyang+mulscale16((int32_t)(((myang+1024-omyang)&2047)-1024),smoothratio);
                    t->sectnum = mycursectnum;
                }
            }

            if( ( display_mirror == 1 || screenpeek != p || s->owner == -1 ) && ud.multimode > 1 && ud.showweapons && sprite[ps[p].i].extra > 0 && ps[p].curr_weapon > 0 )
            {
                memcpy((spritetype *)&tsprite[spritesortcnt],(spritetype *)t,sizeof(spritetype));

                tsprite[spritesortcnt].statnum = 99;

                tsprite[spritesortcnt].yrepeat = ( t->yrepeat>>3 );
                if(t->yrepeat < 4) t->yrepeat = 4;

                tsprite[spritesortcnt].shade = t->shade;
                tsprite[spritesortcnt].cstat = 0;

                switch(ps[p].curr_weapon)
                {
                case PISTOL_WEAPON:      tsprite[spritesortcnt].picnum = FIRSTGUNSPRITE;       break;
                case SHOTGUN_WEAPON:     tsprite[spritesortcnt].picnum = SHOTGUNSPRITE;        break;
                case CHAINGUN_WEAPON:    tsprite[spritesortcnt].picnum = CHAINGUNSPRITE;       break;
                case RPG_WEAPON:         tsprite[spritesortcnt].picnum = RPGSPRITE;            break;
                case HANDREMOTE_WEAPON:
                case HANDBOMB_WEAPON:    tsprite[spritesortcnt].picnum = HEAVYHBOMB;           break;
                case TRIPBOMB_WEAPON:    tsprite[spritesortcnt].picnum = TRIPBOMBSPRITE;       break;
                case GROW_WEAPON:        tsprite[spritesortcnt].picnum = GROWSPRITEICON;       break;
                case SHRINKER_WEAPON:    tsprite[spritesortcnt].picnum = SHRINKERSPRITE;       break;
                case FREEZE_WEAPON:      tsprite[spritesortcnt].picnum = FREEZESPRITE;         break;
                case DEVISTATOR_WEAPON:  tsprite[spritesortcnt].picnum = DEVISTATORSPRITE;     break;
                }

                if(s->owner >= 0)
                    tsprite[spritesortcnt].z = ps[p].posz-(12<<8);
                else tsprite[spritesortcnt].z = s->z-(51<<8);
                if(ps[p].curr_weapon == HANDBOMB_WEAPON)
                {
                    tsprite[spritesortcnt].xrepeat = 10;
                    tsprite[spritesortcnt].yrepeat = 10;
                }
                else
                {
                    tsprite[spritesortcnt].xrepeat = 16;
                    tsprite[spritesortcnt].yrepeat = 16;
                }
                tsprite[spritesortcnt].pal = 0;
                spritesortcnt++;
            }

            if(s->owner == -1)
            {
                k = (((s->ang+3072+128-a)&2047)>>8)&7;
                if(k>4)
                {
                    k = 8-k;
                    t->cstat |= 4;
                }
                else t->cstat &= ~4;

                if(sector[t->sectnum].lotag == 2) k += 1795-1405;
                else if( (hittype[i].floorz-s->z) > (64<<8) ) k += 60;

                t->picnum += k;
                t->pal = ps[p].palookup;

                goto PALONLY;
            }

            if( ps[p].on_crane == -1 && (sector[s->sectnum].lotag&0x7ff) != 1 )
            {
                l = s->z-hittype[ps[p].i].floorz+(3<<8);
                if( l > 1024 && s->yrepeat > 32 && s->extra > 0 )
                    s->yoffset = (int8_t  )(l/(s->yrepeat<<2));
                else s->yoffset=0;
            }

            if(ps[p].newowner > -1)
            {
                t4 = *(actorscrptr[APLAYER]+1);
                t3 = 0;
                t1 = *(actorscrptr[APLAYER]+2);
            }

            if(ud.camerasprite == -1 && ps[p].newowner == -1)
                if(s->owner >= 0 && display_mirror == 0 && ps[p].over_shoulder_on == 0 )
                    if( ud.multimode < 2 || ( ud.multimode > 1 && p == screenpeek ) )
                    {
                        t->owner = -1;
                        t->xrepeat = t->yrepeat = 0;
                        continue;
                    }

        PALONLY:

            if( sector[sect].floorpal )
                t->pal = sector[sect].floorpal;

            if(s->owner == -1) continue;

            if( t->z > hittype[i].floorz && t->xrepeat < 32 )
                t->z = hittype[i].floorz;

            break;

        case JIBS1:
        case JIBS2:
        case JIBS3:
        case JIBS4:
        case JIBS5:
        case JIBS6:
        case HEADJIB1:
        case LEGJIB1:
        case ARMJIB1:
        case LIZMANHEAD1:
        case LIZMANARM1:
        case LIZMANLEG1:
        case DUKELEG:
        case DUKEGUN:
        case DUKETORSO:
            if(ud.lockout)
            {
                t->xrepeat = t->yrepeat = 0;
                continue;
            }
            if(t->pal == 6) t->shade = -120;

        case SCRAP1:
        case SCRAP2:
        case SCRAP3:
        case SCRAP4:
        case SCRAP5:
        case SCRAP6:
        case SCRAP6+1:
        case SCRAP6+2:
        case SCRAP6+3:
        case SCRAP6+4:
        case SCRAP6+5:
        case SCRAP6+6:
        case SCRAP6+7:

            if(hittype[i].picnum == BLIMP && t->picnum == SCRAP1 && s->yvel >= 0)
                t->picnum = s->yvel;
            else t->picnum += T1;
            t->shade -= 6;

            if( sector[sect].floorpal )
                t->pal = sector[sect].floorpal;
            break;

        case WATERBUBBLE:
            if(sector[t->sectnum].floorpicnum == FLOORSLIME)
            {
                t->pal = 7;
                break;
            }
        default:

            if( sector[sect].floorpal )
                t->pal = sector[sect].floorpal;
            break;
        }

        if( actorscrptr[s->picnum] )
        {
            if(t4>10000)
                // FIX_00093: fixed crashbugs in multiplayer (mine/blimp)
                // This is the mine issue (confusion bug in hittype[i].temp_data[4] usage)
                // close to blimp bug (search for BLIMP)
                // -> t4 aka macro T5 is incremented at DETONATEB: in actor.c
                // for a time counter. Instead we want an address.
                // Issue happens in confessn.map (do a dnclip + dnkroz + dncoords,
                // start with duke3d_w32 /m /q2 -map confessn.map)
                // go through the Guilty logo till x = -2932, y = 42174, z = 18416.
                // blow up the bomb. Wait in the water. Look at the respawn sign
                // at the bottom of the chain. Crashes when it's about to respawn.
                // Lame fix. ok for w32. Doesn't work for other plateform.
                // How to make a differene between a timer and an address??
            {
                l = *(int32_t *)(t4+8);

                switch( l )
                {
                case 2:
                    k = (((s->ang+3072+128-a)&2047)>>8)&1;
                    break;

                case 3:
                case 4:
                    k = (((s->ang+3072+128-a)&2047)>>7)&7;
                    if(k > 3)
                    {
                        t->cstat |= 4;
                        k = 7-k;
                    }
                    else t->cstat &= ~4;
                    break;

                case 5:
                    k = getangle(s->x-x,s->y-y);
                    k = (((s->ang+3072+128-k)&2047)>>8)&7;
                    if(k>4)
                    {
                        k = 8-k;
                        t->cstat |= 4;
                    }
                    else t->cstat &= ~4;
                    break;
                case 7:
                    k = getangle(s->x-x,s->y-y);
                    k = (((s->ang+3072+128-k)&2047)/170);
                    if(k>6)
                    {
                        k = 12-k;
                        t->cstat |= 4;
                    }
                    else t->cstat &= ~4;
                    break;
                case 8:
                    k = (((s->ang+3072+128-a)&2047)>>8)&7;
                    t->cstat &= ~4;
                    break;
                default:
                    k = 0;
                    break;
                }

                t->picnum += k + ( *(int32_t *)t4 ) + l * t3;

                if(l > 0)
                    while(tiles[t->picnum].dim.width == 0 && t->picnum > 0 )
                        t->picnum -= l;       //Hack, for actors

                if( hittype[i].dispicnum >= 0)
                    hittype[i].dispicnum = t->picnum;
            }
            else if(display_mirror == 1)
                t->cstat |= 4;
        }

        if( s->statnum == 13 || badguy(s) || (s->picnum == APLAYER && s->owner >= 0) )
            if(t->statnum != 99 && s->picnum != EXPLOSION2 && s->picnum != HANGLIGHT && s->picnum != DOMELITE)
                if(s->picnum != HOTMEAT)
                {
                    if( hittype[i].dispicnum < 0 )
                    {
                        hittype[i].dispicnum++;
                        continue;
                    }
                    else if( ud.shadows && spritesortcnt < (MAXSPRITESONSCREEN-2))
                    {
                        int32_t daz,xrep,yrep;

                        if( (sector[sect].lotag&0xff) > 2 || s->statnum == 4 || s->statnum == 5 || s->picnum == DRONE || s->picnum == COMMANDER )
                            daz = sector[sect].floorz;
                        else
                            daz = hittype[i].floorz;

                        if( (s->z-daz) < (8<<8) )
                            if( ps[screenpeek].posz < daz )
                            {
                                memcpy((spritetype *)&tsprite[spritesortcnt],(spritetype *)t,sizeof(spritetype));

                                tsprite[spritesortcnt].statnum = 99;

                                tsprite[spritesortcnt].yrepeat = ( t->yrepeat>>3 );
                                if(t->yrepeat < 4) t->yrepeat = 4;

                                tsprite[spritesortcnt].shade = 127;
                                tsprite[spritesortcnt].cstat |= 2;

                                tsprite[spritesortcnt].z = daz;
                                xrep = tsprite[spritesortcnt].xrepeat;// - (klabs(daz-t->z)>>11);
                                tsprite[spritesortcnt].xrepeat = xrep;
                                tsprite[spritesortcnt].pal = 4;

                                yrep = tsprite[spritesortcnt].yrepeat;// - (klabs(daz-t->z)>>11);
                                tsprite[spritesortcnt].yrepeat = yrep;
                                spritesortcnt++;
                            }
                    }

                    if( ps[screenpeek].heat_amount > 0 && ps[screenpeek].heat_on )
                    {
                        t->pal = 6;
                        t->shade = 0;
                    }
                }


        switch(s->picnum)
        {
        case LASERLINE:
            if(sector[t->sectnum].lotag == 2) t->pal = 8;
            t->z = sprite[s->owner].z-(3<<8);
            if(lasermode == 2 && ps[screenpeek].heat_on == 0 )
                t->yrepeat = 0;
        case EXPLOSION2:
        case EXPLOSION2BOT:
        case FREEZEBLAST:
        case ATOMICHEALTH:
        case FIRELASER:
        case SHRINKSPARK:
        case GROWSPARK:
        case CHAINGUN:
        case SHRINKEREXPLOSION:
        case RPG:
        case FLOORFLAME:
            if(t->picnum == EXPLOSION2)
            {
                ps[screenpeek].visibility = -127;
                lastvisinc = totalclock+32;
                restorepalette = 1;
            }
            t->shade = -127;
            break;
        case FIRE:
        case FIRE2:
        case BURNING:
        case BURNING2:
            if( sprite[s->owner].picnum != TREE1 && sprite[s->owner].picnum != TREE2 )
                t->z = sector[t->sectnum].floorz;
            t->shade = -127;
            break;
        case COOLEXPLOSION1:
            t->shade = -127;
            t->picnum += (s->shade>>1);
            break;
        case PLAYERONWATER:

            k = (((t->ang+3072+128-a)&2047)>>8)&7;
            if(k>4)
            {
                k = 8-k;
                t->cstat |= 4;
            }
            else t->cstat &= ~4;

            t->picnum = s->picnum+k+((T1<4)*5);
            t->shade = sprite[s->owner].shade;

            break;

        case WATERSPLASH2:
            t->picnum = WATERSPLASH2+t1;
            break;
        case REACTOR2:
            t->picnum = s->picnum + T3;
            break;
        case SHELL:
            t->picnum = s->picnum+(T1&1);
        case SHOTGUNSHELL:
            t->cstat |= 12;
            if(T1 > 1) t->cstat &= ~4;
            if(T1 > 2) t->cstat &= ~12;
            break;
        case FRAMEEFFECT1:
        case FRAMEEFFECT1_13CON:
            if(s->owner >= 0 && sprite[s->owner].statnum < MAXSTATUS)
            {
                if(sprite[s->owner].picnum == APLAYER)
                    if(ud.camerasprite == -1)
                        if(screenpeek == sprite[s->owner].yvel && display_mirror == 0)
                        {
                            t->owner = -1;
                            break;
                        }
                if( (sprite[s->owner].cstat&32768) == 0 )
                {
                    t->picnum = hittype[s->owner].dispicnum;
                    t->pal = sprite[s->owner].pal;
                    t->shade = sprite[s->owner].shade;
                    t->ang = sprite[s->owner].ang;
                    t->cstat = 2|sprite[s->owner].cstat;
                }
            }
            break;

        case CAMERA1:
        case RAT:
            k = (((t->ang+3072+128-a)&2047)>>8)&7;
            if(k>4)
            {
                k = 8-k;
                t->cstat |= 4;
            }
            else t->cstat &= ~4;
            t->picnum = s->picnum+k;
            break;
        }

        hittype[i].dispicnum = t->picnum;
        if(sector[t->sectnum].floorpicnum == MIRROR)
            t->xrepeat = t->yrepeat = 0;
    }
}



#define NUMCHEATCODES 26
uint8_t  cheatquotes[NUMCHEATCODES][14] = {
    {"cornholio"},      // 0
    {"stuff"},          // 1
    {"scotty###"},      // 2
    {"coords"},         // 3
    {"view"},           // 4
    {"time"},           // 5
    {"unlock"},         // 6
    {"cashman"},        // 7
    {"items"},          // 8
    {"rate"},           // 9
    {"skill#"},         // 10
    {"beta"},           // 11
    {"hyper"},          // 12
    {"monsters"},       // 13
    {"<RESERVED>"},     // 14
    {"<RESERVED>"},     // 15
    {"todd"},           // 16
    {"showmap"},        // 17
    {"kroz"},           // 18
    {"allen"},          // 19
    {"clip"},           // 20
    {"weapons"},        // 21
    {"inventory"},      // 22
    {"keys"},           // 23
    {"debug"}           // 24
//    {"ending"}

};


uint8_t  cheatbuf[10],cheatbuflen;
void cheats(void)
{
    short ch, i, j, k, weapon;

    if( (ps[myconnectindex].gm&MODE_TYPE) || (ps[myconnectindex].gm&MODE_MENU))
        return;

    if ( ps[myconnectindex].cheat_phase == 1)
    {
        while (KB_KeyWaiting())
        {
            ch = KB_Getch();
            ch = tolower(ch);

            if( !( (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') ) )
            {
                ps[myconnectindex].cheat_phase = 0;
//             FTA(46,&ps[myconnectindex]);
                return;
            }

            cheatbuf[cheatbuflen++] = ch;
            cheatbuf[cheatbuflen] = 0;

            if(cheatbuflen > 11)
            {
                ps[myconnectindex].cheat_phase = 0;
                return;
            }

            for(k = 0;k < NUMCHEATCODES;k++)
            {
                for(j = 0;j<cheatbuflen;j++)
                {
                    if( cheatbuf[j] == cheatquotes[k][j] || (cheatquotes[k][j] == '#' && ch >= '0' && ch <= '9') )
                    {
                        if( cheatquotes[k][j+1] == 0 ) goto FOUNDCHEAT;
                        if(j == cheatbuflen-1) return;
                    }
                    else break;
                }
            }

            ps[myconnectindex].cheat_phase = 0;
            return;

        FOUNDCHEAT:
            {
                switch(k)
                {
                case 0: // cornholio
                case 18: // kroz

                    ud.god = 1-ud.god;

                    if(ud.god)
                    { // set on
                        pus = 1;
                        pub = 1;
                        sprite[ps[myconnectindex].i].cstat = 257;

                        hittype[ps[myconnectindex].i].temp_data[0] = 0;
                        hittype[ps[myconnectindex].i].temp_data[1] = 0;
                        hittype[ps[myconnectindex].i].temp_data[2] = 0;
                        hittype[ps[myconnectindex].i].temp_data[3] = 0;
                        hittype[ps[myconnectindex].i].temp_data[4] = 0;
                        hittype[ps[myconnectindex].i].temp_data[5] = 0;

                        sprite[ps[myconnectindex].i].hitag = 0;
                        sprite[ps[myconnectindex].i].lotag = 0;
                        sprite[ps[myconnectindex].i].pal =
                            ps[myconnectindex].palookup;

                        FTA(17,&ps[myconnectindex],1);
                    }
                    else // set off
                    {
                        ud.god = 0;
                        sprite[ps[myconnectindex].i].extra = max_player_health;
                        hittype[ps[myconnectindex].i].extra = -1;
                        ps[myconnectindex].last_extra = max_player_health;
                        FTA(18,&ps[myconnectindex],1);
                    }

                    sprite[ps[myconnectindex].i].extra = max_player_health;
                    hittype[ps[myconnectindex].i].extra = 0;
                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();

                    return;

                case 1: // stuff

                    if(VOLUMEONE)
                        j = 6;
                    else
                        j = 0;

                    for ( weapon = PISTOL_WEAPON;weapon < MAX_WEAPONS-j;weapon++ )
                        ps[myconnectindex].gotweapon[weapon]  = 1;

                    for ( weapon = PISTOL_WEAPON;
                          weapon < (MAX_WEAPONS-j);
                          weapon++ )
                        addammo( weapon, &ps[myconnectindex], max_ammo_amount[weapon] );

                    ps[myconnectindex].ammo_amount[GROW_WEAPON] = 50;

                    ps[myconnectindex].steroids_amount =         400;
                    ps[myconnectindex].heat_amount     =        1200;
                    ps[myconnectindex].boot_amount          =    200;
                    ps[myconnectindex].shield_amount =           100;
                    ps[myconnectindex].scuba_amount =            6400;
                    ps[myconnectindex].holoduke_amount =         2400;
                    ps[myconnectindex].jetpack_amount =          1600;
                    ps[myconnectindex].firstaid_amount =         max_player_health;

                    ps[myconnectindex].got_access =              7;
                    FTA(5,&ps[myconnectindex],1);
                    ps[myconnectindex].cheat_phase = 0;

                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    ps[myconnectindex].inven_icon = 1;
                    return;

                case 2:  // dnscotty###
                case 10: // skill#

                    if(k == 2)
                    {
                        short volnume,levnume;
                        volnume = cheatbuf[6] - '0';
                        levnume = (cheatbuf[7] - '0')*10+(cheatbuf[8]-'0');

                        volnume--;
                        levnume--;
                        if (VOLUMEONE)
                        {
                            if( volnume > 0 )
                            {
                                ps[myconnectindex].cheat_phase = 0;
                                KB_FlushKeyboardQueue();
                                return;
                            }
                        }

                        if((volnume > 4)&&PLUTOPAK)
                        {
                            ps[myconnectindex].cheat_phase = 0;
                            KB_FlushKeyboardQueue();
                            return;
                        }
                        else
                            if((volnume > 3)&&!PLUTOPAK)
                            {
                                ps[myconnectindex].cheat_phase = 0;
                                KB_FlushKeyboardQueue();
                                return;
                            }
                            else

                                if(volnume == 0)
                                {
                                    if(levnume > 5)
                                    {
                                        ps[myconnectindex].cheat_phase = 0;
                                        KB_FlushKeyboardQueue();
                                        return;
                                    }
                                }
                                else
                                {
                                    if(levnume >= 11)
                                    {
                                        ps[myconnectindex].cheat_phase = 0;
                                        KB_FlushKeyboardQueue();
                                        return;
                                    }
                                }

                        ud.m_volume_number = ud.volume_number = volnume;
                        ud.m_level_number = ud.level_number = levnume;

                    }
                    else ud.m_player_skill = ud.player_skill =
                             cheatbuf[5] - '1';

                    if(numplayers > 1 && myconnectindex == connecthead)
                    {
                        tempbuf[0] = 5;
                        tempbuf[1] = ud.m_level_number;
                        tempbuf[2] = ud.m_volume_number;
                        tempbuf[3] = ud.m_player_skill;
                        tempbuf[4] = ud.m_monsters_off;
                        tempbuf[5] = ud.m_respawn_monsters;
                        tempbuf[6] = ud.m_respawn_items;
                        tempbuf[7] = ud.m_respawn_inventory;
                        tempbuf[8] = ud.m_coop;
                        tempbuf[9] = ud.m_marker;
                        tempbuf[10] = ud.m_ffire;

                        for(i=connecthead;i>=0;i=connectpoint2[i])
                            sendpacket(i,(uint8_t*)tempbuf,11);
                    }
                    else ps[myconnectindex].gm |= MODE_RESTART;

                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    return;

                case 3: // coords
                    ps[myconnectindex].cheat_phase = 0;
                    ud.coords = 1-ud.coords;
                    KB_FlushKeyboardQueue();
                    return;

                case 4: // view
                    if( ps[myconnectindex].over_shoulder_on )
                        ps[myconnectindex].over_shoulder_on = 0;
                    else
                    {
                        ps[myconnectindex].over_shoulder_on = 1;
                        cameradist = 0;
                        cameraclock = totalclock;
                    }
                    // FTA(22,&ps[myconnectindex],1);
                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    return;

                case 5: // time
                        // FTA(21,&ps[myconnectindex]);
                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    return;

                case 6: // unlock
                    for(i=numsectors-1;i>=0;i--) //Unlock
                    {
                        j = sector[i].lotag;
                        if(j == -1 || j == 32767) continue;
                        if( (j & 0x7fff) > 2 )
                        {
                            if( j&(0xffff-16384) )
                                sector[i].lotag &= (0xffff-16384);
                            operatesectors(i,ps[myconnectindex].i);
                        }
                    }
                    operateforcefields(ps[myconnectindex].i,-1);

                    FTA(100,&ps[myconnectindex],1);
                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    return;

                case 7: // cashman
                    ud.cashman = 1-ud.cashman;
                    KB_ClearKeyDown(sc_N);
                    ps[myconnectindex].cheat_phase = 0;
                    return;

                case 8: // items
                    ps[myconnectindex].steroids_amount =         400;
                    ps[myconnectindex].heat_amount     =        1200;
                    ps[myconnectindex].boot_amount          =    200;
                    ps[myconnectindex].shield_amount =           100;
                    ps[myconnectindex].scuba_amount =            6400;
                    ps[myconnectindex].holoduke_amount =         2400;
                    ps[myconnectindex].jetpack_amount =          1600;

                    ps[myconnectindex].firstaid_amount =         max_player_health;
                    ps[myconnectindex].got_access =              7;
                    FTA(5,&ps[myconnectindex],1);
                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    return;

                case 9: // rate
                    ud.tickrate ^= 1;
                    vscrn(); // FIX_00056: Refresh issue w/FPS, small Weapon and custom FTA, when screen resized down
                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    return;

                case 11: // beta
                    FTA(105,&ps[myconnectindex],1);
                    KB_ClearKeyDown(sc_H);
                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    return;

                case 12: // hyper
                    ps[myconnectindex].steroids_amount = 399;
                    ps[myconnectindex].heat_amount = 1200;
                    ps[myconnectindex].cheat_phase = 0;
                    FTA(37,&ps[myconnectindex],1);
                    KB_FlushKeyboardQueue();
                    return;

                case 13: // monsters
                    if(actor_tog == 3) actor_tog = 0;
                    actor_tog++;
                    ps[screenpeek].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    return;

                case 14: // <RESERVED>
                case 25: // ??
                    ud.eog = 1;
                    ps[myconnectindex].gm |= MODE_EOL;
                    KB_FlushKeyboardQueue();
                    return;

                case 15: // <RESERVED>
                    ps[myconnectindex].gm = MODE_EOL;
                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    return;

                case 16: // todd
                    FTA(99,&ps[myconnectindex],1);
                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    return;

                case 17: // showmap
                    ud.showallmap = 1-ud.showallmap;
                    if(ud.showallmap)
                    {
                        for(i=0;i<(MAXSECTORS>>3);i++)
                            show2dsector[i] = 255;
                        for(i=0;i<(MAXWALLS>>3);i++)
                            show2dwall[i] = 255;
                        FTA(111,&ps[myconnectindex],1);
                    }
                    else
                    {
                        for(i=0;i<(MAXSECTORS>>3);i++)
                            show2dsector[i] = 0;
                        for(i=0;i<(MAXWALLS>>3);i++)
                            show2dwall[i] = 0;
                        FTA(1,&ps[myconnectindex],1);
                    }
                    ps[myconnectindex].cheat_phase = 0;
                    KB_FlushKeyboardQueue();
                    return;

                case 19: // allen
                    FTA(79,&ps[myconnectindex],1);
                    ps[myconnectindex].cheat_phase = 0;
                    KB_ClearKeyDown(sc_N);
                    return;

                case 20: // clip
                    ud.clipping = 1-ud.clipping;
                    KB_FlushKeyboardQueue();
                    ps[myconnectindex].cheat_phase = 0;
                    FTA(112+ud.clipping,&ps[myconnectindex],1);
                    return;

                case 21: // weapons
                    if(VOLUMEONE)
                        j = 6;
                    else
                        j = 0;

                    for ( weapon = PISTOL_WEAPON;weapon < MAX_WEAPONS-j;weapon++ )
                    {
                        addammo( weapon, &ps[myconnectindex], max_ammo_amount[weapon] );
                        ps[myconnectindex].gotweapon[weapon]  = 1;
                    }

                    KB_FlushKeyboardQueue();
                    ps[myconnectindex].cheat_phase = 0;
                    FTA(119,&ps[myconnectindex],1);
                    return;

                case 22: // inventory
                    KB_FlushKeyboardQueue();
                    ps[myconnectindex].cheat_phase = 0;
                    ps[myconnectindex].steroids_amount =         400;
                    ps[myconnectindex].heat_amount     =        1200;
                    ps[myconnectindex].boot_amount          =    200;
                    ps[myconnectindex].shield_amount =           100;
                    ps[myconnectindex].scuba_amount =            6400;
                    ps[myconnectindex].holoduke_amount =         2400;
                    ps[myconnectindex].jetpack_amount =          1600;
                    ps[myconnectindex].firstaid_amount =         max_player_health;
                    FTA(120,&ps[myconnectindex],1);
                    ps[myconnectindex].cheat_phase = 0;
                    return;

                case 23: // keys
                    ps[myconnectindex].got_access =              7;
                    KB_FlushKeyboardQueue();
                    ps[myconnectindex].cheat_phase = 0;
                    FTA(121,&ps[myconnectindex],1);
                    return;

                case 24: // debug
                    debug_on = 1-debug_on;
                    KB_FlushKeyboardQueue();
                    ps[myconnectindex].cheat_phase = 0;
                    break;
                }
            }
        }
    }

    else
    {
        if( KB_KeyPressed(sc_D) )
        {
            if( ps[myconnectindex].cheat_phase >= 0 && numplayers < 2 && ud.recstat == 0)
                ps[myconnectindex].cheat_phase = -1;
        }

        if( KB_KeyPressed(sc_N) )
        {
            if( ps[myconnectindex].cheat_phase == -1 )
            {
                if(ud.player_skill == 4)
                {
                    FTA(22,&ps[myconnectindex],1);
                    ps[myconnectindex].cheat_phase = 0;
                }
                else
                {
                    ps[myconnectindex].cheat_phase = 1;
//                    FTA(25,&ps[myconnectindex]);
                    cheatbuflen = 0;
                }
                KB_FlushKeyboardQueue();
            }
            else if(ps[myconnectindex].cheat_phase != 0)
            {
                ps[myconnectindex].cheat_phase = 0;
                KB_ClearKeyDown(sc_D);
                KB_ClearKeyDown(sc_N);
            }
        }
    }
}


int32_t nonsharedtimer;
void nonsharedkeys(void)
{
    short i,ch;
    int32_t j;
    char text[512];

    if(ud.recstat == 2)
    {
        ControlInfo noshareinfo;
        CONTROL_GetInput( &noshareinfo );
    }

    if( KB_KeyPressed( sc_F12 ) )
    {
        KB_ClearKeyDown( sc_F12 );
        takescreenshot();
        // FTA(103,&ps[myconnectindex]); done better in takescreenshot()
    }

    if( !ALT_IS_PRESSED && ud.overhead_on == 0)
    {
        if( ACTION( gamefunc_Enlarge_Screen ) )
        {
            CONTROL_ClearAction( gamefunc_Enlarge_Screen );
            if(ud.screen_size > 0)
                sound(THUD);

            // FIX_00027: Added an extra small statusbar (HUD)
            if (ud.screen_size==4)
            {
                ud.extended_screen_size++;
                if(ud.extended_screen_size==2)
                {
                    ud.extended_screen_size = 1;
                    ud.screen_size -= 4;
                }
            }
            else
                ud.screen_size -= 4;
            vscrn();
        }
        if( ACTION( gamefunc_Shrink_Screen ) )
        {
            CONTROL_ClearAction( gamefunc_Shrink_Screen );
            if(ud.screen_size < 64) sound(THUD);

            // FIX_00027: Added an extra small statusbar (HUD)
            if (ud.screen_size==4)
            {
                ud.extended_screen_size--;
                if(ud.extended_screen_size<0)
                {
                    ud.extended_screen_size=0;
                    ud.screen_size += 4;
                }
            }
            else
                ud.screen_size += 4;
            vscrn();
        }

        if(ud.screen_size < 4)
            ud.extended_screen_size = 1;
        else if(ud.screen_size > 4)
            ud.extended_screen_size = 0;

    }

    if( ps[myconnectindex].cheat_phase == 1 || ps[myconnectindex].gm&(MODE_MENU|MODE_TYPE)) return;

    if( ACTION(gamefunc_See_Coop_View) && ( ud.coop == 1 || ud.recstat == 2) )
    {
        CONTROL_ClearAction( gamefunc_See_Coop_View );
        screenpeek = connectpoint2[screenpeek];
        if(screenpeek == -1) screenpeek = connecthead;
        restorepalette = 1;
    }

    if( ud.multimode > 1 && ACTION(gamefunc_Show_Opponents_Weapon) )
    {
        CONTROL_ClearAction(gamefunc_Show_Opponents_Weapon);
        ud.showweapons = 1-ud.showweapons;
        FTA(82-ud.showweapons,&ps[screenpeek],1);
    }

    if( ACTION(gamefunc_Toggle_Crosshair) )
    {
        CONTROL_ClearAction(gamefunc_Toggle_Crosshair);
        ud.crosshair = 1-ud.crosshair;
        FTA(21-ud.crosshair,&ps[screenpeek],1);
    }

    if(ud.overhead_on && ACTION(gamefunc_Map_Follow_Mode) )
    {
        CONTROL_ClearAction(gamefunc_Map_Follow_Mode);
        ud.scrollmode = 1-ud.scrollmode;
        if(ud.scrollmode)
        {
            ud.folx = ps[screenpeek].oposx;
            ud.foly = ps[screenpeek].oposy;
            ud.fola = ps[screenpeek].oang;
        }
        FTA(83+ud.scrollmode,&ps[myconnectindex],1);
    }

    if( SHIFTS_IS_PRESSED || ALT_IS_PRESSED )
    {
        i = 0;
        if( KB_KeyPressed( sc_F1) ) { KB_ClearKeyDown(sc_F1);i = 1; }
        if( KB_KeyPressed( sc_F2) ) { KB_ClearKeyDown(sc_F2);i = 2; }
        if( KB_KeyPressed( sc_F3) ) { KB_ClearKeyDown(sc_F3);i = 3; }
        if( KB_KeyPressed( sc_F4) ) { KB_ClearKeyDown(sc_F4);i = 4; }
        if( KB_KeyPressed( sc_F5) ) { KB_ClearKeyDown(sc_F5);i = 5; }
        if( KB_KeyPressed( sc_F6) ) { KB_ClearKeyDown(sc_F6);i = 6; }
        if( KB_KeyPressed( sc_F7) ) { KB_ClearKeyDown(sc_F7);i = 7; }
        if( KB_KeyPressed( sc_F8) ) { KB_ClearKeyDown(sc_F8);i = 8; }
        if( KB_KeyPressed( sc_F9) ) { KB_ClearKeyDown(sc_F9);i = 9; }
        if( KB_KeyPressed( sc_F10) ) {KB_ClearKeyDown(sc_F10);i = 10; }

        if(i)
        {
            if(SHIFTS_IS_PRESSED)
            {
                if(i == 5 && ps[myconnectindex].fta > 0 && ps[myconnectindex].ftq == 26)
                {
                    music_select++;

                    // FIX_00065: Music cycling with F5 and SHIFT-F5 messed up
                    if(VOLUMEALL) // Then its 1.3d reg
                    {
                        if(music_select == 33) music_select = 0;
                    }
                    else if (VOLUMEONE)
                    {
                        if(music_select == 6) music_select = 0;
                    }
                    else // assume 1.5 or plutopak
                    {
                        if(music_select == 44) music_select = 0;
                    }

                    strcpy(text,"PLAYING ");
                    strcat(text,&music_fn[0][music_select][0]);
                    MUSIC_StopSong(); // FIX_00074: Shift f5 doesn't change hi-res tunes, but only midi tunes.
                    playmusic(&music_fn[0][music_select][0]);
                    strcpy(&fta_quotes[26][0],text);
                    FTA(26,&ps[myconnectindex],1);
                    return;
                }

                adduserquote(ud.ridecule[i-1]);

                ch = 0;

                tempbuf[ch] = 4;
                tempbuf[ch+1] = 0;
                strcat((char*)tempbuf+1,ud.ridecule[i-1]);

                i = 1+strlen(ud.ridecule[i-1]);

                if(ud.multimode > 1)
                    for(ch=connecthead;ch>=0;ch=connectpoint2[ch])
                        if (ch != myconnectindex)
                            sendpacket(ch,tempbuf,i);

                pus = NUMPAGES;
                pub = NUMPAGES;

                return;

            }

            if(ud.lockout == 0)
                if(SoundToggle && ALT_IS_PRESSED && ( RTS_NumSounds() > 0 ) && rtsplaying == 0 && VoiceToggle )
                {
                    rtsptr = RTS_GetSound (i-1);
                    if(*rtsptr == 'C')
                        FX_PlayVOC3D( rtsptr,0,0,0,255,-i);
                    else FX_PlayWAV3D( rtsptr,0,0,0,255,-i);

                    rtsplaying = 7;

                    if(ud.multimode > 1)
                    {
                        tempbuf[0] = 7;
                        tempbuf[1] = i;

                        for(ch=connecthead;ch>=0;ch=connectpoint2[ch])
                            if(ch != myconnectindex)
                                sendpacket(ch,(uint8_t*)tempbuf,2);
                    }

                    pus = NUMPAGES;
                    pub = NUMPAGES;

                    return;
                }
        }
    }

    if(!ALT_IS_PRESSED && !SHIFTS_IS_PRESSED)
    {

        if( ud.multimode > 1 && ACTION(gamefunc_SendMessage) )
        {
            KB_FlushKeyboardQueue();
            CONTROL_ClearAction( gamefunc_SendMessage );
            ps[myconnectindex].gm |= MODE_TYPE;
            typebuf[0] = 0;
            inputloc = 0;
        }

        if( KB_KeyPressed(sc_F1) || ( ud.show_help && ( KB_KeyPressed(sc_Space) || KB_KeyPressed(sc_Enter) || KB_KeyPressed(sc_kpad_Enter) ) ) )
        {
            KB_ClearKeyDown(sc_F1);
            KB_ClearKeyDown(sc_Space);
            KB_ClearKeyDown(sc_kpad_Enter);
            KB_ClearKeyDown(sc_Enter);
            ud.show_help ++;

            if( ud.show_help > 2 )
            {
                ud.show_help = 0;
                if(ud.multimode < 2 && ud.recstat != 2) ready2send = 1;
                vscrn();
            }
            else
            {
                setview(0,0,xdim-1,ydim-1);
                if(ud.multimode < 2 && ud.recstat != 2)
                {
                    ready2send = 0;
                    totalclock = ototalclock;
                }
            }
        }

//        if(ud.multimode < 2)
        {
            if(ud.recstat != 2 && KB_KeyPressed( sc_F2 ) )
            {
                KB_ClearKeyDown( sc_F2 );

                if(movesperpacket == 4 && connecthead != myconnectindex)
                    return;

            FAKE_F2:
                if(sprite[ps[myconnectindex].i].extra <= 0)
                {
                    FTA(118,&ps[myconnectindex],1);
                    return;
                }
                cmenu(350);
                screencapt = 1;
                displayrooms(myconnectindex,65536);
                savetemp("duke3d.tmp",tiles[MAXTILES-1].data,160*100);
                screencapt = 0;
                FX_StopAllSounds();
                clearsoundlocks();

//                setview(0,0,xdim-1,ydim-1);
                ps[myconnectindex].gm |= MODE_MENU;

                if(ud.multimode < 2)
                {
                    ready2send = 0;
                    totalclock = ototalclock;
                    screenpeek = myconnectindex;
                }
            }

            if(KB_KeyPressed( sc_F3 ))
            {
                KB_ClearKeyDown( sc_F3 );

                if(movesperpacket == 4 && connecthead != myconnectindex)
                    return;

                cmenu(300);
                FX_StopAllSounds();
                clearsoundlocks();

//                setview(0,0,xdim-1,ydim-1);
                ps[myconnectindex].gm |= MODE_MENU;
                if(ud.multimode < 2 && ud.recstat != 2)
                {
                    ready2send = 0;
                    totalclock = ototalclock;
                }
                screenpeek = myconnectindex;
            }
        }

        if(KB_KeyPressed( sc_F4 ) && FXDevice != NumSoundCards )
        {
            KB_ClearKeyDown( sc_F4 );
            FX_StopAllSounds();
            clearsoundlocks();

            ps[myconnectindex].gm |= MODE_MENU;
            if(ud.multimode < 2 && ud.recstat != 2)
            {
                ready2send = 0;
                totalclock = ototalclock;
            }
            cmenu(700);

        }

        if( KB_KeyPressed( sc_F6 ) && (ps[myconnectindex].gm&MODE_GAME))
        {
            KB_ClearKeyDown( sc_F6 );

            if(movesperpacket == 4 && connecthead != myconnectindex)
                return;

            if(lastsavedpos == -1) goto FAKE_F2;

            KB_FlushKeyboardQueue();

            if(sprite[ps[myconnectindex].i].extra <= 0)
            {
                FTA(118,&ps[myconnectindex],1);
                return;
            }
            screencapt = 1;
            displayrooms(myconnectindex,65536);
            savetemp("duke3d.tmp",tiles[MAXTILES-1].data,160*100);
            screencapt = 0;
            if( lastsavedpos >= 0 )
            {
                inputloc = strlen(&ud.savegame[lastsavedpos][0]);
                current_menu = 360+lastsavedpos;
                probey = lastsavedpos;
            }
            FX_StopAllSounds();
            clearsoundlocks();

            setview(0,0,xdim-1,ydim-1);
            ps[myconnectindex].gm |= MODE_MENU;
            if(ud.multimode < 2 && ud.recstat != 2)
            {
                ready2send = 0;
                totalclock = ototalclock;
            }
        }

        if(KB_KeyPressed( sc_F7 ) )
        {
            KB_ClearKeyDown(sc_F7);
            if( ps[myconnectindex].over_shoulder_on )
                ps[myconnectindex].over_shoulder_on = 0;
            else
            {
                ps[myconnectindex].over_shoulder_on = 1;
                cameradist = 0;
                cameraclock = totalclock;
            }
            FTA(109+ps[myconnectindex].over_shoulder_on,&ps[myconnectindex],1);
        }

        if( KB_KeyPressed( sc_F5 ) && MusicDevice != NumSoundCards )
        {
            KB_ClearKeyDown( sc_F5 );
            strcpy(text,&music_fn[0][music_select][0]);
            strcat(text,".  USE SHIFT-F5 TO CHANGE.");
            strcpy(fta_quotes[26],text);
            FTA(26,&ps[myconnectindex],1);

        }

        if(KB_KeyPressed( sc_F8 ))
        {
            KB_ClearKeyDown( sc_F8 );
            ud.fta_on = !ud.fta_on;
            FTA(24-ud.fta_on,&ps[myconnectindex],1);
        }

        if(KB_KeyPressed( sc_F9 ) && (ps[myconnectindex].gm&MODE_GAME) )
        {
            KB_ClearKeyDown( sc_F9 );

            if(movesperpacket == 4 && myconnectindex != connecthead)
                return;

            if( lastsavedpos >= 0 ) cmenu(15001);
            else cmenu(25000);
            FX_StopAllSounds();
            clearsoundlocks();
            ps[myconnectindex].gm |= MODE_MENU;
            if(ud.multimode < 2 && ud.recstat != 2)
            {
                ready2send = 0;
                totalclock = ototalclock;
            }
        }

        if(KB_KeyPressed( sc_F10 ))
        {
            KB_ClearKeyDown( sc_F10 );
            cmenu(500);
            FX_StopAllSounds();
            clearsoundlocks();
            ps[myconnectindex].gm |= MODE_MENU;
            if(ud.multimode < 2 && ud.recstat != 2)
            {
                ready2send = 0;
                totalclock = ototalclock;
            }
        }


        if( ud.overhead_on != 0)
        {

            j = totalclock-nonsharedtimer; nonsharedtimer += j;
            if ( ACTION( gamefunc_Enlarge_Screen ) )
                ps[myconnectindex].zoom += mulscale6(j,max(ps[myconnectindex].zoom,256));
            if ( ACTION( gamefunc_Shrink_Screen ) )
                ps[myconnectindex].zoom -= mulscale6(j,max(ps[myconnectindex].zoom,256));

            if( (ps[myconnectindex].zoom > 2048) )
                ps[myconnectindex].zoom = 2048;
            if( (ps[myconnectindex].zoom < 48) )
                ps[myconnectindex].zoom = 48;

        }
    }

    if( KB_KeyPressed(sc_Escape) && ud.overhead_on && ps[myconnectindex].newowner == -1 )
    {
        KB_ClearKeyDown( sc_Escape );
        ud.last_overhead = ud.overhead_on;
        ud.overhead_on = 0;
        ud.scrollmode = 0;
        vscrn();
    }

    if( ACTION(gamefunc_AutoRun) )
    {
        CONTROL_ClearAction(gamefunc_AutoRun);
        ud.auto_run = 1-ud.auto_run;
        FTA(85+ud.auto_run,&ps[myconnectindex],1);
    }

    if( ACTION(gamefunc_Map) )
    {
        CONTROL_ClearAction( gamefunc_Map );
        if( ud.last_overhead != ud.overhead_on && ud.last_overhead)
        {
            ud.overhead_on = ud.last_overhead;
            ud.last_overhead = 0;
        }
        else
        {
            ud.overhead_on++;
            if(ud.overhead_on == 3 ) ud.overhead_on = 0;
            ud.last_overhead = ud.overhead_on;
        }
        restorepalette = 1;
        vscrn();
    }

    if(KB_KeyPressed( sc_F11 ))
    {
        KB_ClearKeyDown( sc_F11 );
        // FIX_00030: Brightness step was not the same from the keys vs menu
        if(SHIFTS_IS_PRESSED) ud.brightness-=8; // Keyboard step must be 8, as the brightness cursor step.
        else ud.brightness+=8;

        if (ud.brightness > 56 )
            ud.brightness = 0;
        else if(ud.brightness < 0)
            ud.brightness = 56;

        setbrightness(ud.brightness>>2,&ps[myconnectindex].palette[0]);
        if(ud.brightness < 40) FTA( 29 + (ud.brightness>>3) ,&ps[myconnectindex],1);
        else if(ud.brightness < 80) FTA( 96 + (ud.brightness>>3) - 5,&ps[myconnectindex],1);
    }
}



void comlinehelp(char  **argv)
{
    printf("Command line help.  %s [/flags...]\n",argv[0]);
    puts(" ?, /?         This help message");
    puts(" /l##          Level (1-11)");
    puts(" /v#           Volume (1-4)");
    puts(" /s#           Skill (1-4)");
    puts(" /r            Record demo");
    puts(" /dFILE        Start to play demo FILE");
    puts(" /m            No monsters");
    puts(" /ns           No sound");
    puts(" /nm           No music");
    puts(" /t#           Respawn, 1 = Monsters, 2 = Items, 3 = Inventory, x = All");
    puts(" /c#           MP mode, 1 = DukeMatch(spawn), 2 = Coop, 3 = Dukematch(no spawn)");
    puts(" /q#           Fake multiplayer (2-8 players)");
    puts(" /a            Use player AI (fake multiplayer only)");
    puts(" /i#           Network mode (1/0) (multiplayer only) (default == 1)");
    puts(" /f#           Send fewer packets (1, 2, 4) (multiplayer only)");
    puts(" /gFILE, /g... Use multiple group files (must be last on command line)");
    puts(" /xFILE        Compile FILE (default GAME.CON)");
    puts(" /u#########   User's favorite weapon order (default: 3425689071)");
    puts(" /#            Load and run a game (slot 0-9)");
    puts(" /z            Skip memory check");
    puts(" -map FILE     Use a map FILE");
    puts(" -name NAME    Foward NAME");
    puts(" -net          Net mode game");
    printf("\n");
}

void checkcommandline(int argc,char  **argv)
{
    short i, j;
    char  *c;
    char  kbdKey;

    ud.fta_on = 1;
    ud.god = 0;
    ud.m_respawn_items = 0;
    ud.m_respawn_monsters = 0;
    ud.m_respawn_inventory = 0;
    ud.warp_on = 0;
    ud.cashman = 0;
    ud.m_player_skill = ud.player_skill = 2;
    ud.multimode_bot = 0;

#ifdef BETA
    return;
#endif

    printf("Commands: ");
    i=1;
    while(i < argc)
    {
        printf("%s ",argv[i]);
        i++;
    }
    printf("\n");

    i = 1;

    if(argc > 1)
    {
        while(i < argc)
        {
            c = argv[i];

            if (stricmp(c, "-map") == 0)
            {
                i++;
                strcpy(boardfilename,argv[i]);
                if( strchr(boardfilename,'.') == 0)
                    strcat(boardfilename,".map");
                printf("Using level: '%s'.\n",boardfilename);
                continue;

            }

            if (stricmp(c, "-net") == 0)
            {
                i += 2;  // skip filename.
                // FIX_00044: Markers are now on by default in netgames (as real DOS duke3d)
                ud.m_marker = ud.marker = 1;
                continue;
            }

            if (stricmp(c, "-game_dir") == 0)
            {
                // Get the file name
                i++;
                c = argv[i];
                setGameDir(c);

                // skip over the file name now that we have it.
                i++;

                continue;
            }

            if (stricmp(c, "-stun") == 0)
            {
                g_bStun = 1;

                i++;
                continue;
            }

            if (stricmp(c, "/disableautoaim") == 0)
            {

                printf( "\nThe Host used the /disableautoaim option to turn your Weapon AutoAim OFF\n"
                        "Playing without AutoAim is usually extremely difficult and can make boring\n"
                        "games ruining Duke's playability. Duke3D was not designed to play with\n"
                        "AutoAim OFF like in modern FPS.\n\n"
                        "Do you authorize the HOST to turn your AutoAim OFF (Y/N)? ");

                do
                    kbdKey = getch()|' ';
                while(kbdKey != 'n' && kbdKey != 'y');

                printf("%c \n", kbdKey);

                if(kbdKey == 'y')
                    nHostForceDisableAutoaim = 1;
                else
                    nHostForceDisableAutoaim = 2;

                i++;
                continue;
            }

            if(*c == '-')
            {
                if( *(c+1) == '8' ) eightytwofifty = 1;
                i++;
                continue;
            }

            if(*c == '?')
            {
                comlinehelp(argv);
                Error(EXIT_SUCCESS, "");
            }

            if(*c == '/')
            {
                c++;
                switch(*c)
                {
                case 'x':
                case 'X':
                    c++;
                    if(*c)
                    {
                        if(getGameDir()[0] != '\0'){
                            sprintf(confilename, "%s\\%s", getGameDir(), c);
                        }
                        else{
                            strcpy(confilename,c);
                        }

                        if(SafeFileExists(confilename) == 0){
                            Error(EXIT_SUCCESS, "Could not find con file '%s'.\n",confilename);
                        }
                        else printf("Using con file: '%s'\n",confilename);
                    }
                    break;
                case 'g':
                case 'G':
                    c++;
                    if(*c){
                        char  fullpathgrpfile[16]; // 16 not enough
                        memset(fullpathgrpfile, 0, 16);

                        if( strchr(c,'.') == 0){
                            strcat(c,".grp"); // crap!
                        }

                        if(getGameDir()[0] != '\0'){
                            sprintf(fullpathgrpfile, "%s\\%s", getGameDir(), c);
                        }
                        else{
                            strcpy(fullpathgrpfile, c);
                        }

                        j = initgroupfile(fullpathgrpfile);
                        if( j == -1 )
                            printf("Could not find group file %s.\n",fullpathgrpfile);
                    }

                    break;
                case 'a':
                case 'A':
                    ud.playerai = 1;
                    puts("Other player AI.");
                    break;
                case 'n':
                case 'N':
                    c++;
                    if(*c == 's' || *c == 'S'){
                        CommandSoundToggleOff = 2;
                        puts("Sound off.");
                    }
                    else
                        if(*c == 'm' || *c == 'M'){
                            CommandMusicToggleOff = 1;
                            puts("Music off.");
                        }
                        else{
                            comlinehelp(argv);
                            Error(EXIT_SUCCESS, "");
                        }
                    break;
                case 'i':
                case 'I':
                    c++;
                    if(*c == '0')
                        networkmode = 0;
                    if(*c == '1')
                        networkmode = 1;
                    printf("Network Mode %d\n",networkmode);
                    break;
                case 'c':
                case 'C':
                    c++;
                    if(*c == '1' || *c == '2' || *c == '3' )  // C1 = DM; C2 =COOP
                        ud.m_coop = *c - '0' - 1; // 0 = DM   1 = COOP
                    else ud.m_coop = 0;

                    switch(ud.m_coop)
                    {
                    case 0:
                        puts("Dukematch (spawn).");
                        break;
                    case 1:
                        puts("Cooperative play.");
                        break;
                    case 2:
                        puts("Dukematch (no spawn).");
                        break;
                    }

                    break;
                case 'z':
                case 'Z':
                    memorycheckoveride = 1;
                    break;
                case 'f':
                case 'F':
                    c++;
                    if(*c == '1')
                        movesperpacket = 1;
                    if(*c == '2')
                        movesperpacket = 2;
                    if(*c == '4')
                    {
                        movesperpacket = 4;
                        setpackettimeout(0x3fffffff,0x3fffffff);
                    }
                    break;
                case 't':
                case 'T':
                    c++;
                    if(*c == '1') ud.m_respawn_monsters = 1;
                    else if(*c == '2') ud.m_respawn_items = 1;
                    else if(*c == '3') ud.m_respawn_inventory = 1;
                    else
                    {
                        ud.m_respawn_monsters = 1;
                        ud.m_respawn_items = 1;
                        ud.m_respawn_inventory = 1;
                    }
                    puts("Respawn on.");
                    break;
                case 'm':
                case 'M':
                    if( *(c+1) != 'a' && *(c+1) != 'A' )
                    {
                        ud.m_monsters_off = 1;
                        ud.m_player_skill = ud.player_skill = 0;
                        puts("Monsters off.");
                    }
                    break;
                case 'w':
                case 'W':
                    ud.coords = 1;
                    break;
                case 'q':
                case 'Q':
                    puts("Fake multiplayer mode.");
                    if( *(++c) == 0) ud.multimode_bot = 1;
                    else ud.multimode_bot = atol(c)%17;
                    ud.multimode = ud.multimode_bot;
                    ud.m_coop = ud.coop = 0;
                    ud.m_marker = ud.marker = 1;
                    ud.m_respawn_monsters = ud.respawn_monsters = 1;
                    ud.m_respawn_items = ud.respawn_items = 1;
                    ud.m_respawn_inventory = ud.respawn_inventory = 1;

                    break;
                case 'r':
                case 'R':
                    ud.m_recstat = 1;
                    puts("Demo record mode on.");
                    break;
                case 'd':
                case 'D':
                    c++;
                    if( strchr(c,'.') == 0)
                        strcat(c,".dmo");
                    printf("Play demo %s.\n",c);
                    strcpy(firstdemofile,c);
                    break;
                case 'l':
                case 'L':
                    ud.warp_on = 1;
                    c++;
                    ud.m_level_number = ud.level_number = (atol(c)-1)%11;
                    break;
                case 'j':
                case 'J':
                    Error(EXIT_SUCCESS, "This port has a auto adaptive version system. All versions supported, simply change your duke3d.grp\n");
                    break;

                case 'v':
                case 'V':
                    c++;
                    ud.warp_on = 1;
                    ud.m_volume_number = ud.volume_number = atol(c)-1;
                    break;
                case 's':
                case 'S':
                    c++;
                    ud.m_player_skill = ud.player_skill = (atol(c)%5);
                    if(ud.m_player_skill == 4)
                        ud.m_respawn_monsters = ud.respawn_monsters = 1;
                    break;
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    ud.warp_on = 2 + (*c) - '0';
                    break;
                case 'u':
                case 'U':
                    c++;
                    j = 0;
                    if(*c)
                    {
                        puts("Using favorite weapon order(s).");
                        while(*c)
                        {
                            ud.mywchoice[j] = *c-'0';
                            c++;
                            j++;
                        }
                        while(j < 10)
                        {
                            if(j == 9)
                                ud.mywchoice[9] = 1;
                            else
                                ud.mywchoice[j] = 2;

                            j++;
                        }
                    }
                    else
                    {
                        puts("Using default weapon orders.");
                    }

                    break;

                case '?': // Show help
                default:

                    comlinehelp(argv);
                    Error(EXIT_SUCCESS, "");
                    break;
                }
            }
            i++;
        }
    }
}



void printstr(short x, short y, uint8_t  string[81], uint8_t  attribute)
{
    uint8_t  character;
    short i, pos;

    pos = (y*80+x)<<1;
    i = 0;
    while (string[i] != 0)
    {
        character = string[i];
        printchrasm(0xb8000+(int32_t)pos,1L,(((int32_t)attribute<<8)+(int32_t)character));
        i++;
        pos+=2;
    }
}

void Logo(void)
{
    short i,soundanm;

    soundanm = 0;

    ready2send = 0;

    KB_FlushKeyboardQueue();

    setview(0,0,xdim-1,ydim-1);
    clearview(0L);
    palto(0,0,0,63);

    flushperms();
    nextpage();

    MUSIC_StopSong();

    if(ud.showcinematics && numplayers < 2)
    {
        ////This plays the explosion from the nuclear sign at the beginning.
        if(!VOLUMEONE)
        {
            if(!KB_KeyWaiting() && nomorelogohack == 0)
            {
                getpackets();

#if 1
                playanm("logo.anm",5);
                palto(0,0,0,63);
#endif

#if 0
                playanm("cineov2.anm",1);
                clearview(0L);
                nextpage();
                playanm("vol4e1.anm",8);
                clearview(0L);
                nextpage();
                playanm("vol4e2.anm",10);
                clearview(0L);
                nextpage();
                playanm("vol4e3.anm",11);
                clearview(0L);
                nextpage();
                playanm("DUKETEAM.ANM",4);
                clearview(0L);
                nextpage();
                playanm("cineov3.anm",2);
                clearview(0L);
                nextpage();
                playanm("RADLOGO.ANM",3);
                clearview(0L);
                nextpage();

                playanm("vol41a.anm",6);
                clearview(0L);
                nextpage();
                playanm("vol42a.anm",7);
                clearview(0L);
                nextpage();
                playanm("vol43a.anm",9);
                clearview(0L);
                nextpage();
#endif

                KB_FlushKeyboardQueue();
            }

            clearview(0L);
            nextpage();
        }

        //MIDI start here
        playmusic(&env_music_fn[0][0]);

        //"REALITY IS OUR GAME" Screen
        for(i=0;i<64;i+=7)
            palto(0,0,0,i);
        ps[myconnectindex].palette = drealms;
        palto(0,0,0,63);
        rotatesprite(0,0,65536L,0,DREALMS,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
        nextpage();
        for(i=63;i>0;i-=7)
            palto(0,0,0,i);



        totalclock = 0;
        while( totalclock < (120*7) && !KB_KeyWaiting() )
            getpackets();





        for(i=0;i<64;i+=7)
            palto(0,0,0,i);
        clearview(0L);
        nextpage();
        ps[myconnectindex].palette = titlepal;
        flushperms();
        rotatesprite(0,0,65536L,0,BETASCREEN,0,0,2+8+16+64,0,0,xdim-1,ydim-1);
        KB_FlushKeyboardQueue();
        nextpage();
        for(i=63;i>0;i-=7)
            palto(0,0,0,i);

        totalclock = 0;

        //Animate screen (Duke picture wiht "DUKE" "NUKEM 3D" coming from far away and hitting the screen"
        while(totalclock < (860+120) && !KB_KeyWaiting())
        {
            rotatesprite(0,0,65536L,0,BETASCREEN,0,0,2+8+16+64,0,0,xdim-1,ydim-1);

            if( totalclock > 120 && totalclock < (120+60) )
            {
                if(soundanm == 0)
                {
                    soundanm = 1;
                    sound(PIPEBOMB_EXPLODE);
                }
                rotatesprite(160<<16,104<<16,(totalclock-120)<<10,0,DUKENUKEM,0,0,2+8,0,0,xdim-1,ydim-1);
            }
            else if( totalclock >= (120+60) )
                rotatesprite(160<<16,(104)<<16,60<<10,0,DUKENUKEM,0,0,2+8,0,0,xdim-1,ydim-1);

            if( totalclock > 220 && totalclock < (220+30) )
            {
                if( soundanm == 1)
                {
                    soundanm = 2;
                    sound(PIPEBOMB_EXPLODE);
                }

                rotatesprite(160<<16,(104)<<16,60<<10,0,DUKENUKEM,0,0,2+8,0,0,xdim-1,ydim-1);
                rotatesprite(160<<16,(129)<<16,(totalclock - 220 )<<11,0,THREEDEE,0,0,2+8,0,0,xdim-1,ydim-1);
            }
            else if( totalclock >= (220+30) )
                rotatesprite(160<<16,(129)<<16,30<<11,0,THREEDEE,0,0,2+8,0,0,xdim-1,ydim-1);

            if(PLUTOPAK) // FIX_00064: Cinematics explosions were not right for 1.3/1.3d grp.
            {

                if( totalclock >= 280 && totalclock < 395 )
                {
                    rotatesprite(160<<16,(151)<<16,(410-totalclock)<<12,0,PLUTOPAKSPRITE+1,0,0,2+8,0,0,xdim-1,ydim-1);
                    if(soundanm == 2)
                    {
                        soundanm = 3;
                        sound(FLY_BY);
                    }
                }
                else if( totalclock >= 395 )
                {
                    if(soundanm == 3)
                    {
                        soundanm = 4;
                        sound(PIPEBOMB_EXPLODE);
                    }
                    rotatesprite(160<<16,(151)<<16,30<<11,0,PLUTOPAKSPRITE+1,0,0,2+8,0,0,xdim-1,ydim-1);
                }
            }

            getpackets();
            nextpage();
        }
        // FIX_00077: Menu goes directly to the "NEW GAME" sub-menu when starting new game (Turrican)
        KB_FlushKeyboardQueue();
    }
    else if(numplayers > 1)
    {
        // FIX_00079: "waiting player" screen not showing up (black screen)
        playmusic(&env_music_fn[0][0]);

        ps[myconnectindex].palette = titlepal;
        for(i=63;i>0;i-=7) palto(0,0,0,i);

        rotatesprite(0,0,65536L,0,BETASCREEN,0,0,2+8+16+64,0,0,xdim-1,ydim-1);
        rotatesprite(160<<16,(104)<<16,60<<10,0,DUKENUKEM,0,0,2+8,0,0,xdim-1,ydim-1);
        rotatesprite(160<<16,(129)<<16,30<<11,0,THREEDEE,0,0,2+8,0,0,xdim-1,ydim-1);
        rotatesprite(160<<16,(151)<<16,30<<11,0,PLUTOPAKSPRITE+1,0,0,2+8,0,0,xdim-1,ydim-1);

        gametext(160,190,"WAITING FOR PLAYERS",14,2);
        nextpage();
    }
    else
    {
        // FIX_00091: Main theme starting too early (Bryzian/Turrican)
        playmusic(&env_music_fn[0][0]);
    }

    waitforeverybody();

    flushperms();
    clearview(0L);
    nextpage();

    ps[myconnectindex].palette = palette;
    sound(NITEVISION_ONOFF);

    palto(0,0,0,0);
    clearview(0L);
}

void loadtmb(void)
{
    uint8_t  tmb[8000];
    int32_t fil, l;

    fil = kopen4load("d3dtimbr.tmb",0);

    if(fil == -1)
        return;

    l = kfilelength(fil);

    kread(fil,(uint8_t  *)tmb,l);

    MUSIC_RegisterTimbreBank(tmb);

    kclose(fil);
}

/*
  ===================
  =
  = ShutDown
  =
  ===================
*/

void ShutDown( void )
{
    SoundShutdown();
    MusicShutdown();
    uninittimer();
    uninitengine();
    CONTROL_Shutdown();
    CONFIG_WriteSetup();
    KB_Shutdown();
    CONSOLE_Term();
}


/*
  ===================
  =
  = Startup
  =
  ===================
*/

void compilecons(void)
{
    char  userconfilename[512];

    mymembuf = (char  *)hittype;
    labelcode = (int32_t *)&sector[0];
    label = (char  *)sprite;

    sprintf(userconfilename, "%s", confilename);

    loadefs(userconfilename,mymembuf, 0);

}


void Startup(void)
{
    int i;

    // Init the Console
    CONSOLE_Init();

    KB_Startup();

    CONFIG_GetSetupFilename();
    CONFIG_ReadSetup();

    compilecons();

#ifdef AUSTRALIA
    ud.lockout = 1;
#endif

    if(CommandSoundToggleOff) SoundToggle = 0;
    if(CommandMusicToggleOff) MusicToggle = 0;

//if(VOLUMEONE)
//{
//   printf("\n*** You have run Duke Nukem 3D %ld times. ***\n",ud.executions);
//   if(ud.executions >= 50) puts("IT IS NOW TIME TO UPGRADE TO THE COMPLETE VERSION!!!\n");
//}

    CONTROL_Startup( ControllerType, &GetTime, TICRATE );

// CTW - MODIFICATION
// initengine(ScreenMode,ScreenWidth,ScreenHeight);
    initengine();
// CTW END - MODIFICATION
    inittimer(TICRATE);

    puts("Loading art header.");

    loadpics("tiles000.art", "\0");

    readsavenames();

    tiles[MIRROR].dim.width = tiles[MIRROR].dim.height = 0;

    for(i=0;i<MAXPLAYERS;i++) playerreadyflag[i] = 0;
    initmultiplayers(0,0,0);

    if(numplayers > 1)
        puts("Multiplayer initialized.");

    ps[myconnectindex].palette = (uint8_t  *) &palette[0];
    SetupGameButtons();

    if(networkmode == 255)
        networkmode = 1;

    /* SBF - wasn't sure if swapping them would harm anything. */
    puts("Checking sound inits.");
    SoundStartup();
    puts("Checking music inits.");
    MusicStartup();

    // AutoAim
    if(nHostForceDisableAutoaim)
        ud.auto_aim = 0;

    puts("loadtmb()");
    loadtmb();
}


void sendscore(char  *s)
{
    if(numplayers > 1)
        genericmultifunction(-1,s,strlen(s)+1,5);
}


void getnames(void)
{
    short i,j,l;

    // FIX_00031: Names now limited to 10 chars max that is the fragbar field limit.
    for(l=0; l<=9 && myname[l];l++)
    {
        ud.user_name[myconnectindex][l] = toupper(myname[l]);
        buf[l+2] = toupper(myname[l]);
    }

#ifdef CHECK_XDUKE_REV // must not be under "if(numplayers > 1)" so it runs in any case
    ud.rev[myconnectindex][0] = true; // always true. Used to check who we validated
    ud.rev[myconnectindex][1] = DUKE_ID;
    ud.rev[myconnectindex][2] = CHOCOLATE_DUKE_REV_X;
    ud.rev[myconnectindex][3] = CHOCOLATE_DUKE_REV_DOT_Y;
#endif

    memcpy(ud.groupefil_crc32[myconnectindex],groupefil_crc32, sizeof(groupefil_crc32));
    ud.conSize[myconnectindex] = ud.conSize[0]; // [0] still containing the original value
    ud.conCRC[myconnectindex] = ud.conCRC[0];
    ud.exeCRC[myconnectindex] = ud.exeCRC[0];

    if(numplayers > 1)
    {

        buf[0] = 6;
        buf[1] = grpVersion;

        buf[l+2] = 0;
        l += 3;

        for(i=connecthead;i>=0;i=connectpoint2[i])
            if( i != myconnectindex )
                sendpacket(i,(uint8_t*)buf,l);

        if(nHostForceDisableAutoaim==2) // user doesn't want AA off.
            for(i=connecthead;i>=0;i=connectpoint2[i])
            {
                buf[0] = 133; // request to stop the game.
                sendpacket(i,(uint8_t*)buf,l);
            }


#ifdef CHECK_XDUKE_REV
        buf[0] = 131; // xDuke TAG ID
        buf[1] = ud.rev[myconnectindex][1];
        buf[2] = ud.rev[myconnectindex][2]; // version x
        buf[3] = ud.rev[myconnectindex][3]; // version .y

        buf[4] = 0;             // reserved
        buf[5] = 0;             // reserved...
        buf[9] = 0;             // reserved.
        // See below for single player mode.

        for(i=connecthead;i>=0;i=connectpoint2[i])
            if( i != myconnectindex )
                sendpacket(i,&buf[0],10);
#endif

        //      getpackets();


        l = 1;
        buf[0] = 9; // send weapon order

        for(i=0;i<10;i++)
        {
            ud.wchoice[myconnectindex][i] = ud.mywchoice[i];
            buf[l] = (uint8_t ) ud.mywchoice[i];
            l++;
        }

        for(i=connecthead;i>=0;i=connectpoint2[i])
            if(i != myconnectindex)
                sendpacket(i,(uint8_t*)&buf[0],11);

        buf[0] = 134;   // GRP CRC + CON SIZE + conCRC + exeCRC
        memcpy(buf+1, groupefil_crc32, sizeof(groupefil_crc32));
        memcpy(buf+1+sizeof(groupefil_crc32), ud.conSize, sizeof(ud.conSize[0]));
        memcpy(buf+1+sizeof(groupefil_crc32)+sizeof(ud.conSize[0]), ud.conCRC, sizeof(ud.conCRC[0]));
        memcpy(buf+1+sizeof(groupefil_crc32)+sizeof(ud.conSize[0])+sizeof(ud.conCRC[0]), ud.exeCRC, sizeof(ud.exeCRC[0]));

        for(i=connecthead;i>=0;i=connectpoint2[i])
            if( i != myconnectindex )
                sendpacket(i,(uint8_t*)buf,1+sizeof(groupefil_crc32)+sizeof(ud.conSize[0])+sizeof(ud.conCRC[0])+
                           sizeof(ud.exeCRC[0]));

//        getpackets();

        buf[0] = 10;
        buf[1] = ps[0].aim_mode;
        ps[myconnectindex].aim_mode = ps[0].aim_mode;

        for(i=connecthead;i>=0;i=connectpoint2[i])
            if(i != myconnectindex)
                sendpacket(i,(uint8_t*)buf,2);

//        getpackets();

        if(cp == 0)
        {
            buf[0] = 125;

            for(i=connecthead;i>=0;i=connectpoint2[i])
                if(i != myconnectindex)
                    sendpacket(i,(uint8_t*)buf,1);
        }

//        getpackets();

        waitforeverybody();

#ifdef CHECK_XDUKE_REV
        // from command "case 131:"
        for(l=0,i=connecthead;i>=0;i=connectpoint2[i])
            if(((ud.rev[i][2]<<8)+ud.rev[i][3]) != ((CHOCOLATE_DUKE_REV_X<<8)+CHOCOLATE_DUKE_REV_DOT_Y))
                l=1;
            else
                ud.rev[i][0] = true; // means we validated this guy

        if(l)
        {
            printf("\n*** One or more players do not have the same xDuke version:\n\n");
            for(l=0,i=connecthead;i>=0;i=connectpoint2[i])
                printf("Player [%-10s] is using Chocolate DukeNukem3D v%d.%d\n", ud.user_name[i],
                       ud.rev[i][2],ud.rev[i][3]);
            Error(EXIT_SUCCESS, "");
        }
#endif

        // checking GRP/CON size from "case 134"
        for(l=0,i=connecthead;i>=0;i=connectpoint2[i])
            for(j=0; j<MAXGROUPFILES; j++)
            {
                if(ud.groupefil_crc32[i][j]!=ud.groupefil_crc32[myconnectindex][j] || ud.conSize[i] != ud.conSize[myconnectindex])
                    l=1;
            }

        if(l)
        {
            printf("\n*** One or more players do not have the same GRP/CON version:\n\n");
            for(i=connecthead;i>=0;i=connectpoint2[i])
            {
                for(j=0; j<MAXGROUPFILES && ud.groupefil_crc32[i][j]; j++)
                {
                    if(j)
                        printf("                    GRP (Add-on) : %s CRC=%X\n",
                               grpVersion2char_from_crc(ud.groupefil_crc32[i][j]),
                               ud.groupefil_crc32[i][j]);
                    else
                        printf("Player [%-10s] GRP (base)   : %s CRC=%X\n", ud.user_name[i],
                               grpVersion2char_from_crc(ud.groupefil_crc32[i][j]),
                               ud.groupefil_crc32[i][j]);
                }
                printf("                    CON code size: %d\n\n",ud.conSize[i]);
            }
            Error(EXIT_SUCCESS,     "");
        }
    }
    else if(nHostForceDisableAutoaim==2)
    {
        nHostForceDisableAutoaim=0;
        ud.auto_aim = 2;
    }

    if(cp == 1)
        gameexit("Please put Duke Nukem 3D Atomic Edition CD in drive.");
}


const char* const baseDir="duke3d*.grp";

int dukeGRP_Match(char* filename,int length)
{
    char* cursor = filename+length-4;

    if (strncasecmp(cursor,".grp",4))
        return 0;

    return !strncasecmp(filename,"duke3d",6);
}


void findGRPToUse(char * groupfilefullpath){

    char directoryToScan[512];
    struct dirent* dirEntry ;

    directoryToScan[0] = '\0';

    if (getGameDir()[0] != '\0')
    {
        strcat(directoryToScan,getGameDir());
        if (directoryToScan[strlen(directoryToScan)-1] != '/')
            strcat(directoryToScan,"/");
    }
    else{
        strcat(directoryToScan, "/");
    }

    printf("Scanning directory '%s' for a GRP file like '%s'.\n",directoryToScan,baseDir);

    DIR* dir =  opendir(directoryToScan);

    while ((dirEntry = readdir(dir)) != NULL)
    {
            if (dukeGRP_Match(dirEntry->d_name,strlen(dirEntry->d_name)))
            {
                sprintf(groupfilefullpath,"%s%s",directoryToScan,dirEntry->d_name);
                return;
            }

    }
}

static int load_duke3d_groupfile(void)
{
    // FIX_00032: Added multi base GRP manager. Use duke3d*.grp to handle multiple grp.

    char  groupfilefullpath[512];
    groupfilefullpath[0] = '\0';

    findGRPToUse(groupfilefullpath);

    if (groupfilefullpath[0] == '\0')
        return false;


    FixFilePath(groupfilefullpath);

    return(initgroupfile(groupfilefullpath) != -1);
}

#ifdef COMBINED_SDL
#define main duke3d_main
#else
#define main my_main
#endif

int main(int argc,char  **argv)
{
    int32_t i, j;
    int32_t filehandle;


    uint8_t  kbdKey;
    uint8_t  *exe;


    //printf(       "This is a debug version 19.7.1 only Based on 19.7\n"
    //              "Fully compliant with v19.7. Added the following:\n\n"
    //              "FIX_00086: grp loaded by smaller sucessive chunks to avoid\n"
    //              "           overloading low ram computers (Spanator)\n"
    //              "FIX_00087: intro in 1024x768 mode being slow. Undone FIX_00070\n"
    //              "           and fixed font issue again (Bryzian)\n"
    //              "FIX_00088: crash on maps using a bad palette index like the end\n"
    //              "           of roch3.map (NY00123)\n"
    //              "FIX_00089: scoreboard not shown for last player who quits a DM.\n"
    //              "           Only 19.7 affected. (Sarah)\n"
    //              "FIX_00090: Removed showinfo key. FPS were shown after CRC msg. \n"
    //              "           CRC not always removed. (Turrican)\n"
    //              "FIX_00091: Main theme starting too early (Bryzian/Turrican)\n"
    //              "FIX_00092: corrupted saved files making the following saved\n"
    //              "           files invisible (Bryzian)\n\n"
    //              "This version should not be distributed. It's not secret but it\n"
    //              "would create a bad mess in the duke community if people start\n"
    //              "using it as it may contain new unsuspected bugs. Only a select\n"
    //              "group of known dukers who know what they are doing should be using\n"
    //              "it. Please report new bugs at xd@m-klein.com or on DX forums. Thx!\n\n");

    printf("*** Chocolate DukeNukem3D v%d.%d ***\n\n", CHOCOLATE_DUKE_REV_X, CHOCOLATE_DUKE_REV_DOT_Y);

    // FIX_00033: Fake multi and AI are now fully working
    ud.multimode = 1;  // xduke: must be done before checkcommandline or that will prevent Fakeplayer and AI

    if (!load_duke3d_groupfile())
    {
        Error(EXIT_SUCCESS, "Could not initialize any original BASE duke3d*.grp file\n"
              "Even if you are playing a custom GRP you still need\n"
                                                        "an original base GRP file as Shareware/Full 1.3D GRP or\n"
                                                        "the v1.5 ATOMIC GRP file. Such a file seems to be missing\n"
                                                        "or is corrupted\n");
    }

    rb->splashf(0, "point 1");
        // FIX_00022: Automatically recognize the shareware grp (v1.3) + full version (1.3d) +
        //            atomic (1.4/1.5 grp) and the con files version (either 1.3 or 1.4) (JonoF's idea)

        // Detecting grp version
        // We keep the old GRP scheme detection for 19.6 compliance. Will be obsolete.
        filehandle = kopen4load("DUKEDC9.MAP",1);
        kclose(filehandle);

    rb->splashf(0, "point 2");
        if (filehandle == -1) // not DC pack
        {
    rb->splashf(0, "point 3");
                filehandle = kopen4load("DUKESW.BIN",1);
                kclose(filehandle);
    rb->splashf(0, "point 4");

                if (filehandle == -1) // not Shareware version 1.3
                {
    rb->splashf(0, "point 5");
                        filehandle = kopen4load("E4L11.MAP",1);
                        kclose(filehandle);
    rb->splashf(0, "point 6");

                        if (filehandle == -1) // not Atomic Edition 1.4/1.5
                        {
    rb->splashf(0, "point 7");
                                filehandle = kopen4load("E3L11.MAP",1);
                                kclose(filehandle);

    rb->splashf(0, "point 8");

                                if (filehandle == -1) // not Regular version 1.3d
                                {
                                        grpVersion = UNKNOWN_GRP;
                                }
                                else
                                {
                                        grpVersion = REGULAR_GRP13D;
                                }
                        }
                        else
                        {
                                grpVersion = ATOMIC_GRP14_15;
                        }
                }
                else
                {
                        grpVersion = SHAREWARE_GRP13;
                }
        }
        else
        {
                grpVersion = DUKEITOUTINDC_GRP;
        }

    rb->splashf(0, "point 9");
        // FIX_00062: Better support and identification for GRP and CON files for 1.3/1.3d/1.4/1.5
        if (    groupefil_crc32[0]==CRC_BASE_GRP_SHAREWARE_13 ||
                                groupefil_crc32[0]==CRC_BASE_GRP_FULL_13 ||
                                groupefil_crc32[0]==CRC_BASE_GRP_PLUTONIUM_14 ||
                                groupefil_crc32[0]==CRC_BASE_GRP_ATOMIC_15 )
        {
    rb->splashf(0, "point 10");
                printf("GRP identified as: %s\n", grpVersion2char_from_crc(groupefil_crc32[0]));
        }
        else
        {
    rb->splashf(0, "point 11");
                printf( "The content of your original BASE *.GRP is corrupted. CRC=%X\n"
                        "You may run in troubles. Official GRP are:\n\n", groupefil_crc32[0]);

                for(i=0; i<MAX_KNOWN_GRP; i++)
                        printf("%s -> CRC32=%X  Size=%d bytes\n", crc32lookup[i].name, crc32lookup[i].crc32, crc32lookup[i].size);

#if 0
                printf( "\nYou should try to get one of these GRP only as a base GRP\n"
                                "Do you want to continue anyway? (Y/N): ");
                do
                        kbdKey = getch() | ' ';
                while(kbdKey != 'y' && kbdKey != 'n');
                printf("%c\n", kbdKey);

                if(kbdKey == 'n')
                        Error(EXIT_SUCCESS,"");
#endif
                rb->splashf(HZ, "WARNING: Your GRP is not a well-known version. Continue at your own risk!");
        }

        // computing exe crc
        ud.exeCRC[0] = 0;
        exe = NULL;
        //filehandle = open(argv[0],O_BINARY|O_RDONLY);
        filehandle = -1;
        if(filehandle!=-1)
        {
                exe = malloc(filelength(filehandle));
                if(exe)
                {
                        read(filehandle, exe, filelength(filehandle));
                        ud.exeCRC[0] = crc32_update(exe, filelength(filehandle), ud.exeCRC[0]);
                        free(exe);
                }
                close(filehandle);
        }

        checkcommandline(argc,argv);

    _platform_init(argc, argv, "Duke Nukem 3D", "Duke3D");

    totalmemory = Z_AvailHeap();

    if(memorycheckoveride == 0)
    {
        if(totalmemory < (3162000-350000))
        {
            puts("You don't have enough free memory to run Duke Nukem 3D.");
            puts("The DOS \"mem\" command should report 6,800K (or 6.8 megs)");
            puts("of \"total memory free\".\n");
            printf("Duke Nukem 3D requires %d more bytes to run.\n",3162000-350000-totalmemory);
            Error(EXIT_SUCCESS, "");
        }
    }
    else
        printf("Using %d bytes for heap.\n",totalmemory);

    RegisterShutdownFunction( ShutDown );

    Startup();

    if( eightytwofifty && numplayers > 1 && (MusicDevice != NumSoundCards) )
    {
        puts("\n=========================================================================");
        puts("WARNING: 8250 UART detected.");
        puts("Music is being disabled and lower quality sound is being set.  We apologize");
        puts("for this, but it is necessary to maintain high frame rates while trying to");
        puts("play the game on an 8250.  We suggest upgrading to a 16550 or better UART");
        puts("for maximum performance.  Press any key to continue.");
        puts("=========================================================================\n");

        while( !KB_KeyWaiting() ) getpackets();
    }

        if(g_bStun)
        {
                waitforeverybody();
        }

    if(numplayers > 1) // if multimode > 1 and numplayer == 1 => fake player mode on
    {
        ud.multimode = numplayers;
        sendlogon();
    }
    else if(boardfilename[0] != 0)
    {
        ud.m_level_number = 7;
        ud.m_volume_number = 0;
        ud.warp_on = 1;
    }

    /* crashes */
    //rb->sleep(HZ/5);
    ud.last_level = -1;

    getnames();

    //if(ud.multimode > 1)
    //{
    //  playerswhenstarted = ud.multimode;

                // AddFaz fix.
                // This would cause monsters not to spawn when loading a usermap
                /*
        if(ud.warp_on == 0)
        {
            ud.m_monsters_off = 1;
            ud.m_player_skill = 0;
        }
                */
//}


   RTS_Init(ud.rtsname);
   if(numlumps)
           printf("Using .RTS file:%s\n",ud.rtsname);

   if (CONTROL_JoystickEnabled)
       CONTROL_CenterJoystick(CenterCenter,UpperLeft,LowerRight,CenterThrottle,CenterRudder);

   puts("Loading palette/lookups.");
    if( setgamemode(ScreenMode,ScreenWidth,ScreenHeight) < 0 )
    {
        printf("\nVESA driver for ( %i * %i ) not found/supported!\n",xdim,ydim);
        ScreenMode = 2;
        ScreenWidth = 320;
        ScreenHeight = 200;
        setgamemode(ScreenMode,ScreenWidth,ScreenHeight);
    }

    printf("genspriteremaps()\n");
    genspriteremaps();

    extern bool printf_enabled;
    printf_enabled = false;

    setbrightness(ud.brightness>>2,&ps[myconnectindex].palette[0]);

    if(KB_KeyPressed( sc_Escape ) )
                gameexit(" ");

    FX_StopAllSounds();
    clearsoundlocks();

    if(ud.warp_on > 1 && ud.multimode < 2)
    {
        clearview(0L);
        ps[myconnectindex].palette = palette;
        palto(0,0,0,0);
        rotatesprite(320<<15,200<<15,65536L,0,LOADSCREEN,0,0,2+8+64,0,0,xdim-1,ydim-1);
        menutext(160,105,0,0,"LOADING SAVED GAME...");
        nextpage();

        j = loadplayer(ud.warp_on-2);
        if(j)
            ud.warp_on = 0;
    }

MAIN_LOOP_RESTART:

    if(ud.warp_on == 0) //if game is loaded without /V or /L cmd arguments.
    {

        if (numplayers > 1 && boardfilename[0] != 0) //check if a user map is loaded and in multiplayer.
        {
            int c;

            ud.level_number = ud.m_level_number = 7; // 7 = usermap.
            ud.volume_number = ud.m_volume_number;
            ud.player_skill = ud.m_player_skill;

            switch(ud.m_coop) //set item spawn options, as they would be if
            {      //game was started via main menu.
            case 0:
                ud.respawn_inventory = ud.m_respawn_inventory = 1;
                ud.respawn_items = ud.m_respawn_items = 1;
                break;
            case 1:
                ud.respawn_inventory = ud.m_respawn_inventory = 1;
                ud.respawn_items = ud.m_respawn_items = 0;
                break;
            case 2:
                ud.respawn_inventory = ud.m_respawn_inventory = 0;
                ud.respawn_items = ud.m_respawn_items = 0;
                break;
            }

            if( ud.m_player_skill == 4 )
            {
                ud.m_respawn_monsters = 1; //set skill
            }
            else
            {
                ud.m_respawn_monsters = 0;
            }

            waitforeverybody();

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

            newgame(ud.m_volume_number,ud.m_level_number,ud.m_player_skill);

            enterlevel(MODE_GAME); //start game.

        }
        else
        {
            Logo(); //play logo, (game must be started via menus).
        }
    }



        else if(ud.warp_on == 1) //if cmd arguments /V and /L are given.
    {

                if (numplayers > 1) //if in multiplayer reset everyones weapon status.
                {
                        int c;

            switch(ud.m_coop)   //set item spawn options, as they would be if
                        {                                       //game was started via main menu.
                                case 0:
                                        ud.respawn_inventory = ud.m_respawn_inventory = 1;
                                        ud.respawn_items = ud.m_respawn_items = 1;
                                        break;
                                case 1:
                                        ud.respawn_inventory = ud.m_respawn_inventory = 1;
                                        ud.respawn_items = ud.m_respawn_items = 0;
                                        break;
                                case 2:
                                        ud.respawn_inventory = ud.m_respawn_inventory = 0;
                                        ud.respawn_items = ud.m_respawn_items = 0;
                                        break;
                        }

                        if( ud.m_player_skill == 4 )
                        {
                                ud.m_respawn_monsters = 1; //set skill
                        }
                        else
                        {
                                ud.m_respawn_monsters = 0;
                        }

                        waitforeverybody();

                        for(c=connecthead;c>=0;c=connectpoint2[c])
            {
                            resetweapons(c); //without this players would spawn with no weapon.
                                resetinventory(c);
            }

                }

                newgame(ud.m_volume_number,ud.m_level_number,ud.m_player_skill);
                enterlevel(MODE_GAME); //start game.

    }
    else
        {
                vscrn();
        }

    if( ud.warp_on == 0 && playback() )
    {
        FX_StopAllSounds();
        clearsoundlocks();
        nomorelogohack = 1;
        goto MAIN_LOOP_RESTART;
    }

    ud.warp_on = 0;

        //The main game loop is here.
    while ( !(ps[myconnectindex].gm&MODE_END) )
    {
        sampletimer();
        if( ud.recstat == 2 || ud.multimode > 1 || ( ud.show_help == 0 && (ps[myconnectindex].gm&MODE_MENU) != MODE_MENU ) )
            if( ps[myconnectindex].gm&MODE_GAME )
                        {
                // (" It's stuck here ")
                                //printf("ps[myconnectindex].gm&MODE_GAME\n");
                                if( moveloop() )
                                {
                                        continue;
                                }
                        }

        if( ps[myconnectindex].gm&MODE_EOL || ps[myconnectindex].gm&MODE_RESTART )
        {

            if( ps[myconnectindex].gm&MODE_EOL )
            {
                closedemowrite();

                ready2send = 0;

                i = ud.screen_size;
                ud.screen_size = 0;
                vscrn();
                ud.screen_size = i;
                dobonus(0);

                if(ud.eog)
                {
                    ud.eog = 0;
                    if(ud.multimode < 2)
                    {
                                                if(VOLUMEONE)
                                doorders();

                        ps[myconnectindex].gm = MODE_MENU;
                        cmenu(0);
                        probey = 0;
                        goto MAIN_LOOP_RESTART;
                    }
                    else
                    {
                        ud.m_level_number = 0;
                        ud.level_number = 0;
                    }
                }
            }

            ready2send = 0;
            if(numplayers > 1) ps[myconnectindex].gm = MODE_GAME;

            enterlevel(ps[myconnectindex].gm);
            continue;
        }

        cheats();

        if( !CONSOLE_IsActive() )
          nonsharedkeys();


        if( (ud.show_help == 0 && ud.multimode < 2 && !(ps[myconnectindex].gm&MODE_MENU) ) || ud.multimode > 1 || ud.recstat == 2)
            i = min(max((totalclock-ototalclock)*(65536L/TICSPERFRAME),0),65536);
        else
            i = 65536;

        displayrooms(screenpeek,i);
        displayrest(i);

        if(ps[myconnectindex].gm&MODE_DEMO)
            goto MAIN_LOOP_RESTART;

        if(debug_on)
                        caches();

        checksync();

                if (VOLUMEONE)
                if(ud.show_help == 0 && show_shareware > 0 && (ps[myconnectindex].gm&MODE_MENU) == 0 )
                rotatesprite((320-50)<<16,9<<16,65536L,0,BETAVERSION,0,0,2+8+16+128,0,0,xdim-1,ydim-1);

        nextpage();
    }

    gameexit(" ");
        return(0);
}

uint8_t  opendemoread(uint8_t  which_demo) // 0 = mine
{
    char  d[] = "demo_.dmo";
    char  *fname = d;
    uint8_t  ver;
    short i,j;

        int32 dummy;
        int32_t groupefil_crc32_from_demo[MAXGROUPFILES];

    if(which_demo == 10)
        d[4] = 'x';
    else
        d[4] = '0' + which_demo;

    ud.reccnt = 0;

     if(which_demo == 1 && firstdemofile[0] != 0)
     {
                fname = firstdemofile;
                if ((recfilep = TCkopen4load(firstdemofile,0)) == -1)
                {
                        return(0);
                }
     }
     else
         {
                 if ((recfilep = TCkopen4load(d,0)) == -1)
                 {
                        return(0);
                 }
         }

     kread(recfilep,&ud.reccnt,sizeof(int32_t));
     kread(recfilep,&ver,sizeof(uint8_t ));

         printf("%s has version = %d\n", fname, ver);

        // FIX_00015: Backward compliance with older demos (down to demos v27, 28, 116, 117 and 118)
        if (PLUTOPAK)
        {
                if( (ver != BYTEVERSION && ver != BYTEVERSION_116 && ver != BYTEVERSION_117 && ver != BYTEVERSION_118) ) // || (ud.reccnt < 512) )
                {
                        printf("%s is a demo version %d. We want v. %d, %d, %d, or %d (1.5 Atomic versions)\n",
                                        fname, (int) ver, BYTEVERSION_116, BYTEVERSION_117, BYTEVERSION_118, BYTEVERSION);
                        kclose(recfilep);
                        return 0;
                }
        }
        else // 1.3/1.3d style
        {
                if( (ver != BYTEVERSION && ver != BYTEVERSION_27 && ver != BYTEVERSION_28 && ver != BYTEVERSION_29) ) // || (ud.reccnt < 512) )
                {
                        printf("%s is a demo version %d. We want v. %d, %d, %d or %d (1.3/1.3d versions)\n",
                                        fname, (int) ver, BYTEVERSION_27, BYTEVERSION_28, BYTEVERSION_29, BYTEVERSION);
                        kclose(recfilep);
                        return 0;
                }
        }

        // FIX_00062: Better support and identification for GRP and CON files for 1.3/1.3d/1.4/1.5
        if(ver==BYTEVERSION)
        {
                kread(recfilep, (int32_t *)groupefil_crc32_from_demo, sizeof(groupefil_crc32_from_demo));

                for(i=0; i<MAXGROUPFILES; i++)
                        if(groupefil_crc32_from_demo[i]!=groupefil_crc32[i])
                        {
                                for(j=0; j<=i; j++)
                                {
                                        printf("You have GRP #%d:  %s (CRC32=%X)\n"
                                                   "this demo expects %s (CRC32=%X)\n",
                                                j, grpVersion2char_from_crc(groupefil_crc32[j]),groupefil_crc32[j],
                                                grpVersion2char_from_crc(groupefil_crc32_from_demo[j]),groupefil_crc32_from_demo[j]);

                                }
                                kclose(recfilep);
                                return 0;
                        }


        }

         ud.playing_demo_rev = ver;

         kread(recfilep,(uint8_t  *)&ud.volume_number,sizeof(uint8_t ));
     kread(recfilep,(uint8_t  *)&ud.level_number,sizeof(uint8_t ));
     kread(recfilep,(uint8_t  *)&ud.player_skill,sizeof(uint8_t ));
     kread(recfilep,(uint8_t  *)&ud.m_coop,sizeof(uint8_t ));
     kread(recfilep,(uint8_t  *)&ud.m_ffire,sizeof(uint8_t ));
     kread(recfilep,(short *)&ud.multimode,sizeof(short));
     kread(recfilep,(short *)&ud.m_monsters_off,sizeof(short));
     kread(recfilep,(int32 *)&ud.m_respawn_monsters,sizeof(int32));
     kread(recfilep,(int32 *)&ud.m_respawn_items,sizeof(int32));
     kread(recfilep,(int32 *)&ud.m_respawn_inventory,sizeof(int32));
     kread(recfilep,(int32 *)&ud.playerai,sizeof(int32));
     kread(recfilep,(uint8_t  *)&ud.user_name[0][0],sizeof(ud.user_name));
         // FIX_00034: Demos do not turn your run mode off anymore:
     kread(recfilep,(int32 *)&dummy /*ud.auto_run*/,sizeof(int32)); // not needed and would affect autorun status in duke3d.cfg when quitting duke from a demo
     kread(recfilep,(uint8_t  *)boardfilename,sizeof(boardfilename));
     if( boardfilename[0] != 0 )
     {
        ud.m_level_number = 7;
        ud.m_volume_number = 0;
     }

     for(i=0;i<ud.multimode;i++)
         {
        kread(recfilep,(int32 *)&ps[i].aim_mode,sizeof(uint8_t ));

                // FIX_00080: Out Of Synch in demos. Tries recovering OOS in old demos v27/28/29/116/117/118. New: v30/v119.
                if(ver==BYTEVERSION)
                        kread(recfilep,ud.wchoice[i],sizeof(ud.wchoice[0]));
         }

     ud.god = ud.cashman = ud.eog = ud.showallmap = 0;
     ud.clipping = ud.scrollmode = ud.overhead_on = 0;
         // FIX_00034: Demos do not turn your run mode off anymore:
     /* ud.showweapons =  */ ud.pause_on /*= ud.auto_run */ = 0; // makes no sense to reset those 2 value!

         newgame(ud.volume_number,ud.level_number,ud.player_skill);
         return(1);
}


void opendemowrite(void)
{
    char  d[] = "demo1.dmo";
    int32_t dummylong = 0;
    uint8_t  ver;
    short i;
        char  fullpathdemofilename[16];

    if(ud.recstat == 2) kclose(recfilep);

    ver = BYTEVERSION;

        // Are we loading a TC?
        if(getGameDir()[0] != '\0'){
                // Yes
                sprintf(fullpathdemofilename, "%s\\%s", getGameDir(), d);
        }
        else{
                // No
                sprintf(fullpathdemofilename, "%s", d);
        }

// CTW - MODIFICATION
//  if ((frecfilep = fopen(d,"wb")) == -1) return;
    if ((frecfilep = fopen(fullpathdemofilename,"wb")) == NULL) return;
// CTW END - MODIFICATION
    fwrite(&dummylong,4,1,frecfilep);
    fwrite(&ver,sizeof(uint8_t ),1,frecfilep);
        // FIX_00062: Better support and identification for GRP and CON files for 1.3/1.3d/1.4/1.5
        fwrite((int32_t *)groupefil_crc32,sizeof(groupefil_crc32),1,frecfilep);
    fwrite((uint8_t  *)&ud.volume_number,sizeof(uint8_t ),1,frecfilep);
    fwrite((uint8_t  *)&ud.level_number,sizeof(uint8_t ),1,frecfilep);
    fwrite((uint8_t  *)&ud.player_skill,sizeof(uint8_t ),1,frecfilep);
    fwrite((uint8_t  *)&ud.m_coop,sizeof(uint8_t ),1,frecfilep);
    fwrite((uint8_t  *)&ud.m_ffire,sizeof(uint8_t ),1,frecfilep);
    fwrite((short *)&ud.multimode,sizeof(short),1,frecfilep);
    fwrite((short *)&ud.m_monsters_off,sizeof(short),1,frecfilep);
    fwrite((int32 *)&ud.m_respawn_monsters,sizeof(int32),1,frecfilep);
    fwrite((int32 *)&ud.m_respawn_items,sizeof(int32),1,frecfilep);
    fwrite((int32 *)&ud.m_respawn_inventory,sizeof(int32),1,frecfilep);
    fwrite((int32 *)&ud.playerai,sizeof(int32),1,frecfilep);
    fwrite((uint8_t  *)&ud.user_name[0][0],sizeof(ud.user_name),1,frecfilep);
    fwrite((int32 *)&ud.auto_run,sizeof(int32),1,frecfilep);
    fwrite((uint8_t  *)boardfilename,sizeof(boardfilename),1,frecfilep);

    for(i=0;i<ud.multimode;i++)
        {
                fwrite((int32 *)&ps[i].aim_mode,sizeof(uint8_t ),1,frecfilep); // seems wrong; prolly not needed anyway
                // FIX_00080: Out Of Synch in demos. Tries recovering OOS in old demos v27/28/29/116/117/118. New: v30/v119.
                fwrite(ud.wchoice[i],sizeof(ud.wchoice[0]),1,frecfilep);
        }

    totalreccnt = 0;
    ud.reccnt = 0;
}

void record(void)
{
    short i;
    for(i=connecthead;i>=0;i=connectpoint2[i])
         {
         copybufbyte(&sync[i],&recsync[ud.reccnt],sizeof(input));
                           ud.reccnt++;
                 totalreccnt++;
                 if (ud.reccnt >= RECSYNCBUFSIZ)
                 {
              dfwrite(recsync,sizeof(input)*ud.multimode,ud.reccnt/ud.multimode,frecfilep);
                          ud.reccnt = 0;
                 }
         }
}

void closedemowrite(void)
{
         if (ud.recstat == 1)
         {
        if (ud.reccnt > 0)
        {
            dfwrite(recsync,sizeof(input)*ud.multimode,ud.reccnt/ud.multimode,frecfilep);

            fseek(frecfilep,SEEK_SET,0L);
            fwrite(&totalreccnt,sizeof(int32_t),1,frecfilep);
            ud.recstat = ud.m_recstat = 0;
        }
        fclose(frecfilep);
        frecfilep = NULL;
    }
}

// CTW - MODIFICATION
// On my XP machine, demo playback causes the game to crash shortly in.
// Only bug found so far, not sure if it's OS dependent or compiler or what.
// Seems to happen when player input starts being simulated, but just guessing.
// This change effectively disables it. The related code is still enabled.
// (This is working on Linux, so I flipped it back to '1'. --ryan.)
 uint8_t  which_demo = 0;
// CTW END - MODIFICATION

uint8_t  in_menu = 0;

// extern int32_t syncs[];
int32_t playback(void)
{
    int32_t i,j,k,l,t;
    uint8_t  foundemo;

    if( ready2send )
        {
                return 0;
        }

        /*
        if(numplayers > 1)
                return 1;
        */

    foundemo = 0;

    RECHECK:

    in_menu = ps[myconnectindex].gm&MODE_MENU;

    pub = NUMPAGES;
    pus = NUMPAGES;

    flushperms();

        if(numplayers < 2 && ud.multimode_bot<2) foundemo = opendemoread(which_demo);

    if(foundemo == 0)
    {

        if(which_demo > 1)
        {
            which_demo = 1;
            goto RECHECK;
        }
        for(t=0;t<63;t+=7) palto(0,0,0,t);
        drawbackground();

        CONSOLE_HandleInput();
        if( !CONSOLE_IsActive())
        {
            menus();
        }
        CONSOLE_Render();
        ps[myconnectindex].palette = palette;
        nextpage();
        for(t=63;t>0;t-=7)
                {
                        palto(0,0,0,t);
                }

        ud.reccnt = 0;
    }
    else
    {
        ud.recstat = 2;
        which_demo++;
        if(which_demo == 10)
                {
                        which_demo = 1;
                }

        enterlevel(MODE_DEMO);
    }

    if(foundemo == 0 || in_menu || KB_KeyWaiting() || numplayers > 1)
    {
        FX_StopAllSounds();
        clearsoundlocks();
        ps[myconnectindex].gm |= MODE_MENU;
    }

    ready2send = 0;
    i = 0;

    KB_FlushKeyboardQueue();

    k = 0;

    while (ud.reccnt > 0 || foundemo == 0)
    {

        if(foundemo) while ( totalclock >= (lockclock+TICSPERFRAME) )
        {
            if ((i == 0) || (i >= RECSYNCBUFSIZ))
            {
                i = 0;
                l = min(ud.reccnt,RECSYNCBUFSIZ);
                kdfread(recsync,sizeof(input)*ud.multimode,l/ud.multimode,recfilep);
            }

            for(j=connecthead;j>=0;j=connectpoint2[j])
            {
               copybufbyte(&recsync[i],&inputfifo[movefifoend[j]&(MOVEFIFOSIZ-1)][j],sizeof(input));

                           movefifoend[j]++;
               i++;
               ud.reccnt--;
            }
            domovethings();
        }

        if(foundemo == 0)
            drawbackground();
        else
        {
            if( !CONSOLE_IsActive() )
            {
                nonsharedkeys();
            }

            j = min(max((totalclock-lockclock)*(65536/TICSPERFRAME),0),65536);
            displayrooms(screenpeek,j);
            displayrest(j);

            if(ud.multimode > 1 && ps[myconnectindex].gm )
                getpackets();
        }

        if( (ps[myconnectindex].gm&MODE_MENU) && (ps[myconnectindex].gm&MODE_EOL) )
                {
                        printf("playback(1) :: goto RECHECK:\n");
                        goto RECHECK;
                }

        if(ps[myconnectindex].gm&MODE_TYPE)
        {
            typemode();
            if((ps[myconnectindex].gm&MODE_TYPE) != MODE_TYPE)
                ps[myconnectindex].gm = MODE_MENU;
        }
        else
        {
            CONSOLE_HandleInput();
            if( !CONSOLE_IsActive())
            {
                menus();
            }
            CONSOLE_Render();
            if( ud.multimode > 1 )
            {
                ControlInfo noshareinfo;
                if( !CONSOLE_IsActive() )
                {
                    CONTROL_GetInput( &noshareinfo );
                    if( ACTION(gamefunc_SendMessage) )
                    {
                        KB_FlushKeyboardQueue();
                        CONTROL_ClearAction( gamefunc_SendMessage );
                        ps[myconnectindex].gm = MODE_TYPE;
                        typebuf[0] = 0;
                        inputloc = 0;
                    }
                }

            }
        }

        operatefta();

        if(ud.last_camsprite != ud.camerasprite)
        {
            ud.last_camsprite = ud.camerasprite;
            ud.camera_time = totalclock+(TICRATE*2);
        }

                if (VOLUMEONE)
                        if( ud.show_help == 0 && (ps[myconnectindex].gm&MODE_MENU) == 0 )
                                rotatesprite((320-50)<<16,9<<16,65536L,0,BETAVERSION,0,0,2+8+16+128,0,0,xdim-1,ydim-1);

                getpackets();
        nextpage();

        if( ps[myconnectindex].gm==MODE_END || ps[myconnectindex].gm==MODE_GAME )
        {
            if(foundemo)
                kclose(recfilep);
            ud.playing_demo_rev = 0;
                        return 0;
        }
    }
    kclose(recfilep);
        ud.playing_demo_rev = 0;
    if(ps[myconnectindex].gm&MODE_MENU)
        {
                goto RECHECK;
        }

    return 1;
}

uint8_t  moveloop()
{
    int32_t i;

    if (numplayers > 1)
        {
                while (fakemovefifoplc < movefifoend[myconnectindex])
                {
                        fakedomovethings();
                }
        }


    getpackets();

    if (numplayers < 2) bufferjitter = 0;
    while (movefifoend[myconnectindex]-movefifoplc > bufferjitter)
    {
        for(i=connecthead;i>=0;i=connectpoint2[i])
            if (movefifoplc == movefifoend[i]) break;
        if (i >= 0) break;
        if( domovethings() ) return 1;
    }
    return 0;
}

void fakedomovethingscorrect(void)
{
     int32_t i;
     struct player_struct *p;

     if (numplayers < 2) return;

     i = ((movefifoplc-1)&(MOVEFIFOSIZ-1));
     p = &ps[myconnectindex];

     if (p->posx == myxbak[i] && p->posy == myybak[i] && p->posz == myzbak[i]
          && p->horiz == myhorizbak[i] && p->ang == myangbak[i]) return;

     myx = p->posx; omyx = p->oposx; myxvel = p->posxv;
     myy = p->posy; omyy = p->oposy; myyvel = p->posyv;
     myz = p->posz; omyz = p->oposz; myzvel = p->poszv;
     myang = p->ang; omyang = p->oang;
     mycursectnum = p->cursectnum;
     myhoriz = p->horiz; omyhoriz = p->ohoriz;
     myhorizoff = p->horizoff; omyhorizoff = p->ohorizoff;
     myjumpingcounter = p->jumping_counter;
     myjumpingtoggle = p->jumping_toggle;
     myonground = p->on_ground;
     myhardlanding = p->hard_landing;
     myreturntocenter = p->return_to_center;

     fakemovefifoplc = movefifoplc;
     while (fakemovefifoplc < movefifoend[myconnectindex])
          fakedomovethings();

}

void fakedomovethings(void)
{
        input *syn;
        struct player_struct *p;
        int32_t i, j, k, doubvel, fz, cz, hz, lz, x, y;
        uint32_t sb_snum;
        short psect, psectlotag, tempsect, backcstat;
        uint8_t  shrunk, spritebridge;

        syn = (input *)&inputfifo[fakemovefifoplc&(MOVEFIFOSIZ-1)][myconnectindex];

        p = &ps[myconnectindex];

        backcstat = sprite[p->i].cstat;
        sprite[p->i].cstat &= ~257;

        sb_snum = syn->bits;

        psect = mycursectnum;
        psectlotag = sector[psect].lotag;
        spritebridge = 0;

        shrunk = (sprite[p->i].yrepeat < 32);

        if( ud.clipping == 0 && ( sector[psect].floorpicnum == MIRROR || psect < 0 || psect >= MAXSECTORS) )
        {
            myx = omyx;
            myy = omyy;
        }
        else
        {
            omyx = myx;
            omyy = myy;
        }

        omyhoriz = myhoriz;
        omyhorizoff = myhorizoff;
        omyz = myz;
        omyang = myang;

        getzrange(myx,myy,myz,psect,&cz,&hz,&fz,&lz,163L,CLIPMASK0);

        j = getflorzofslope(psect,myx,myy);

        if( (lz&49152) == 16384 && psectlotag == 1 && klabs(myz-j) > PHEIGHT+(16<<8) )
            psectlotag = 0;

        if( p->aim_mode == 0 && myonground && psectlotag != 2 && (sector[psect].floorstat&2) )
        {
                x = myx+(sintable[(myang+512)&2047]>>5);
                y = myy+(sintable[myang&2047]>>5);
                tempsect = psect;
                updatesector(x,y,&tempsect);
                if (tempsect >= 0)
                {
                     k = getflorzofslope(psect,x,y);
                     if (psect == tempsect)
                          myhorizoff += mulscale16(j-k,160);
                     else if (klabs(getflorzofslope(tempsect,x,y)-k) <= (4<<8))
                          myhorizoff += mulscale16(j-k,160);
                }
        }
        if (myhorizoff > 0) myhorizoff -= ((myhorizoff>>3)+1);
        else if (myhorizoff < 0) myhorizoff += (((-myhorizoff)>>3)+1);

        if(hz >= 0 && (hz&49152) == 49152)
        {
                hz &= (MAXSPRITES-1);
                if (sprite[hz].statnum == 1 && sprite[hz].extra >= 0)
                {
                    hz = 0;
                    cz = getceilzofslope(psect,myx,myy);
                }
        }

        if(lz >= 0 && (lz&49152) == 49152)
        {
                 j = lz&(MAXSPRITES-1);
                 if ((sprite[j].cstat&33) == 33)
                 {
                        psectlotag = 0;
                        spritebridge = 1;
                 }
                 if(badguy(&sprite[j]) && sprite[j].xrepeat > 24 && klabs(sprite[p->i].z-sprite[j].z) < (84<<8) )
                 {
                    j = getangle( sprite[j].x-myx,sprite[j].y-myy);
                    myxvel -= sintable[(j+512)&2047]<<4;
                    myyvel -= sintable[j&2047]<<4;
                }
        }

        if( sprite[p->i].extra <= 0 )
        {
                 if( psectlotag == 2 )
                 {
                            if(p->on_warping_sector == 0)
                            {
                                     if( klabs(myz-fz) > (PHEIGHT>>1))
                                             myz += 348;
                            }
                            clipmove(&myx,&myy,&myz,&mycursectnum,0,0,164L,(4L<<8),(4L<<8),CLIPMASK0);
                 }

                 updatesector(myx,myy,&mycursectnum);
                 pushmove(&myx,&myy,&myz,&mycursectnum,128L,(4L<<8),(20L<<8),CLIPMASK0);

                myhoriz = 100;
                myhorizoff = 0;

                 goto ENDFAKEPROCESSINPUT;
        }

        doubvel = TICSPERFRAME;

        if(p->on_crane >= 0) goto FAKEHORIZONLY;

        if(p->one_eighty_count < 0) myang += 128;

        i = 40;

        if( psectlotag == 2)
        {
                 myjumpingcounter = 0;

                 if ( sb_snum&1 )
                 {
                            if(myzvel > 0) myzvel = 0;
                            myzvel -= 348;
                            if(myzvel < -(256*6)) myzvel = -(256*6);
                 }
                 else if (sb_snum&(1<<1))
                 {
                            if(myzvel < 0) myzvel = 0;
                            myzvel += 348;
                            if(myzvel > (256*6)) myzvel = (256*6);
                 }
                 else
                 {
                    if(myzvel < 0)
                    {
                        myzvel += 256;
                        if(myzvel > 0)
                            myzvel = 0;
                    }
                    if(myzvel > 0)
                    {
                        myzvel -= 256;
                        if(myzvel < 0)
                            myzvel = 0;
                    }
                }

                if(myzvel > 2048) myzvel >>= 1;

                 myz += myzvel;

                 if(myz > (fz-(15<<8)) )
                            myz += ((fz-(15<<8))-myz)>>1;

                 if(myz < (cz+(4<<8)) )
                 {
                            myz = cz+(4<<8);
                            myzvel = 0;
                 }
        }

        else if(p->jetpack_on)
        {
                 myonground = 0;
                 myjumpingcounter = 0;
                 myhardlanding = 0;

                 if(p->jetpack_on < 11)
                            myz -= (p->jetpack_on<<7); //Goin up

                 if(shrunk) j = 512;
                 else j = 2048;

                 if (sb_snum&1)                            //A
                            myz -= j;
                 if (sb_snum&(1<<1))                       //Z
                            myz += j;

                 if(shrunk == 0 && ( psectlotag == 0 || psectlotag == 2 ) ) k = 32;
                 else k = 16;

                 if(myz > (fz-(k<<8)) )
                            myz += ((fz-(k<<8))-myz)>>1;
                 if(myz < (cz+(18<<8)) )
                            myz = cz+(18<<8);
        }
        else if( psectlotag != 2 )
        {
            if (psectlotag == 1 && p->spritebridge == 0)
            {
                 if(shrunk == 0) i = 34;
                 else i = 12;
            }
                 if(myz < (fz-(i<<8)) && (floorspace(psect)|ceilingspace(psect)) == 0 ) //falling
                 {
                            if( (sb_snum&3) == 0 && myonground && (sector[psect].floorstat&2) && myz >= (fz-(i<<8)-(16<<8) ) )
                                     myz = fz-(i<<8);
                            else
                            {
                                     myonground = 0;

                                     myzvel += (gc+80);

                                     if(myzvel >= (4096+2048)) myzvel = (4096+2048);
                            }
                 }

                 else
                 {
                            if(psectlotag != 1 && psectlotag != 2 && myonground == 0 && myzvel > (6144>>1))
                                 myhardlanding = myzvel>>10;
                            myonground = 1;

                            if(i==40)
                            {
                                     //Smooth on the ground

                                     k = ((fz-(i<<8))-myz)>>1;
                                     if( klabs(k) < 256 ) k = 0;
                                     myz += k; // ((fz-(i<<8))-myz)>>1;
                                     myzvel -= 768; // 412;
                                     if(myzvel < 0) myzvel = 0;
                            }
                            else if(myjumpingcounter == 0)
                            {
                                myz += ((fz-(i<<7))-myz)>>1; //Smooth on the water
                                if(p->on_warping_sector == 0 && myz > fz-(16<<8))
                                {
                                    myz = fz-(16<<8);
                                    myzvel >>= 1;
                                }
                            }

                            if( sb_snum&2 )
                                     myz += (2048+768);

                            if( (sb_snum&1) == 0 && myjumpingtoggle == 1)
                                     myjumpingtoggle = 0;

                            else if( (sb_snum&1) && myjumpingtoggle == 0 )
                            {
                                     if( myjumpingcounter == 0 )
                                             if( (fz-cz) > (56<<8) )
                                             {
                                                myjumpingcounter = 1;
                                                myjumpingtoggle = 1;
                                             }
                            }
                            if( myjumpingcounter && (sb_snum&1) == 0 )
                                myjumpingcounter = 0;
                 }

                 if(myjumpingcounter)
                 {
                            if( (sb_snum&1) == 0 && myjumpingtoggle == 1)
                                     myjumpingtoggle = 0;

                            if( myjumpingcounter < (1024+256) )
                            {
                                     if(psectlotag == 1 && myjumpingcounter > 768)
                                     {
                                             myjumpingcounter = 0;
                                             myzvel = -512;
                                     }
                                     else
                                     {
                                             myzvel -= (sintable[(2048-128+myjumpingcounter)&2047])/12;
                                             myjumpingcounter += 180;

                                             myonground = 0;
                                     }
                            }
                            else
                            {
                                     myjumpingcounter = 0;
                                     myzvel = 0;
                            }
                 }

                 myz += myzvel;

                 if(myz < (cz+(4<<8)) )
                 {
                            myjumpingcounter = 0;
                            if(myzvel < 0) myxvel = myyvel = 0;
                            myzvel = 128;
                            myz = cz+(4<<8);
                 }

        }

        if ( p->fist_incs ||
                     p->transporter_hold > 2 ||
                     myhardlanding ||
                     p->access_incs > 0 ||
                     p->knee_incs > 0 ||
                     (p->curr_weapon == TRIPBOMB_WEAPON &&
                      p->kickback_pic > 1 &&
                      p->kickback_pic < 4 ) )
        {
                 doubvel = 0;
                 myxvel = 0;
                 myyvel = 0;
        }
        else if ( syn->avel )          //p->ang += syncangvel * constant
        {                         //ENGINE calculates angvel for you
            int32_t tempang;

            tempang = syn->avel<<1;

            if(psectlotag == 2)
                myang += (tempang-(tempang>>3))*sgn(doubvel);
            else myang += (tempang)*sgn(doubvel);
            myang &= 2047;
        }

        if ( myxvel || myyvel || syn->fvel || syn->svel )
        {
                 if(p->steroids_amount > 0 && p->steroids_amount < 400)
                     doubvel <<= 1;

                 myxvel += ((syn->fvel*doubvel)<<6);
                 myyvel += ((syn->svel*doubvel)<<6);

                 if( ( p->curr_weapon == KNEE_WEAPON && p->kickback_pic > 10 && myonground ) || ( myonground && (sb_snum&2) ) )
                 {
                            myxvel = mulscale16(myxvel,dukefriction-0x2000);
                            myyvel = mulscale16(myyvel,dukefriction-0x2000);
                 }
                 else
                 {
                    if(psectlotag == 2)
                    {
                        myxvel = mulscale16(myxvel,dukefriction-0x1400);
                        myyvel = mulscale16(myyvel,dukefriction-0x1400);
                    }
                    else
                    {
                        myxvel = mulscale16(myxvel,dukefriction);
                        myyvel = mulscale16(myyvel,dukefriction);
                    }
                 }

                 if( abs(myxvel) < 2048 && abs(myyvel) < 2048 )
                     myxvel = myyvel = 0;

                 if( shrunk )
                 {
                     myxvel =
                         mulscale16(myxvel,(dukefriction)-(dukefriction>>1)+(dukefriction>>2));
                     myyvel =
                         mulscale16(myyvel,(dukefriction)-(dukefriction>>1)+(dukefriction>>2));
                 }
        }

FAKEHORIZONLY:
        if(psectlotag == 1 || spritebridge == 1) i = (4L<<8); else i = (20L<<8);

        clipmove(&myx,&myy,&myz,&mycursectnum,myxvel,myyvel,164L,4L<<8,i,CLIPMASK0);
        pushmove(&myx,&myy,&myz,&mycursectnum,164L,4L<<8,4L<<8,CLIPMASK0);

        if( p->jetpack_on == 0 && psectlotag != 1 && psectlotag != 2 && shrunk)
            myz += 30<<8;

        if ((sb_snum&(1<<18)) || myhardlanding)
            myreturntocenter = 9;

        if (sb_snum&(1<<13))
        {
                myreturntocenter = 9;
                if (sb_snum&(1<<5)) myhoriz += 6;
                myhoriz += 6;
        }
        else if (sb_snum&(1<<14))
        {
                myreturntocenter = 9;
                if (sb_snum&(1<<5)) myhoriz -= 6;
                myhoriz -= 6;
        }
        else if (sb_snum&(1<<3))
        {
                if (sb_snum&(1<<5)) myhoriz += 6;
                myhoriz += 6;
        }
        else if (sb_snum&(1<<4))
        {
                if (sb_snum&(1<<5)) myhoriz -= 6;
                myhoriz -= 6;
        }

        if (myreturntocenter > 0)
            if ((sb_snum&(1<<13)) == 0 && (sb_snum&(1<<14)) == 0)
        {
             myreturntocenter--;
             myhoriz += 33-(myhoriz/3);
        }

        if(p->aim_mode)
            myhoriz += syn->horz>>1;
        else
        {
            if( myhoriz > 95 && myhoriz < 105) myhoriz = 100;
            if( myhorizoff > -5 && myhorizoff < 5) myhorizoff = 0;
        }

        if (myhardlanding > 0)
        {
            myhardlanding--;
            myhoriz -= (myhardlanding<<4);
        }

        if (myhoriz > 299) myhoriz = 299;
        else if (myhoriz < -99) myhoriz = -99;

        if(p->knee_incs > 0)
        {
            myhoriz -= 48;
            myreturntocenter = 9;
        }


ENDFAKEPROCESSINPUT:

        myxbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myx;
        myybak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myy;
        myzbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myz;
        myangbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myang;
        myhorizbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myhoriz;
        fakemovefifoplc++;

        sprite[p->i].cstat = backcstat;
}


uint8_t  domovethings(void)
{
    short i, j;
    uint8_t  ch;


    for(i=connecthead;i>=0;i=connectpoint2[i])
        if( sync[i].bits&(1<<17) )
    {
        multiflag = 2;
        multiwhat = (sync[i].bits>>18)&1;
        multipos = (uint32_t) (sync[i].bits>>19)&15;
        multiwho = i;

        if( multiwhat )
        {
                        // FIX_00058: Save/load game crash in both single and multiplayer
            screencapt = 1;
            displayrooms(myconnectindex,65536);
            savetemp("duke3d.tmp",tiles[MAXTILES-1].data,160*100);
            screencapt = 0;

            saveplayer( multipos );
            multiflag = 0;

            if(multiwho != myconnectindex)
            {
                strcpy(fta_quotes[122],&ud.user_name[multiwho][0]);
                strcat(fta_quotes[122]," SAVED A MULTIPLAYER GAME");
                FTA(122,&ps[myconnectindex],1);
            }
            else
            {
                strcpy(fta_quotes[122],"MULTIPLAYER GAME SAVED");
                FTA(122,&ps[myconnectindex],1);
            }
            break;
        }
        else
        {
//            waitforeverybody();

            j = loadplayer( multipos );

            multiflag = 0;

            if(j == 0)
            {
                if(multiwho != myconnectindex)
                {
                    strcpy(fta_quotes[122],&ud.user_name[multiwho][0]);
                    strcat(fta_quotes[122]," LOADED A MULTIPLAYER GAME");
                    FTA(122,&ps[myconnectindex],1);
                }
                else
                {
                    strcpy(fta_quotes[122],"MULTIPLAYER GAME LOADED");
                    FTA(122,&ps[myconnectindex],1);
                }
                return 1;
            }
        }
    }

    ud.camerasprite = -1;
    lockclock += TICSPERFRAME;

    if(earthquaketime > 0) earthquaketime--;
    if(rtsplaying > 0) rtsplaying--;

    for(i=0;i<MAXUSERQUOTES;i++)
         if (user_quote_time[i])
         {
             user_quote_time[i]--;
             if (!user_quote_time[i]) pub = NUMPAGES;
         }
     if ((klabs(quotebotgoal-quotebot) <= 16) && (ud.screen_size <= 8))
         quotebot += ksgn(quotebotgoal-quotebot);
     else
         quotebot = quotebotgoal;

    if( show_shareware > 0 )
    {
        show_shareware--;
        if(show_shareware == 0)
        {
            pus = NUMPAGES;
            pub = NUMPAGES;
        }
    }

    everyothertime++;

    for(i=connecthead;i>=0;i=connectpoint2[i])
        copybufbyte(&inputfifo[movefifoplc&(MOVEFIFOSIZ-1)][i],&sync[i],sizeof(input));
    movefifoplc++;

    updateinterpolations();

    j = -1;
    for(i=connecthead;i>=0;i=connectpoint2[i])
     {
          if ((sync[i].bits&(1<<26)) == 0) { j = i; continue; }

          closedemowrite();

          if (i == myconnectindex) gameexit(" ");
          if (screenpeek == i)
          {
                screenpeek = connectpoint2[i];
                if (screenpeek < 0) screenpeek = connecthead;
          }

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

          numplayers--;
          ud.multimode--;

          if (numplayers < 2)
              sound(GENERIC_AMBIENCE17);

          pub = NUMPAGES;
          pus = NUMPAGES;
          vscrn();

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

          quickkill(&ps[i]);
          deletesprite(ps[i].i);

          adduserquote(buf);

          if(j < 0 && networkmode == 0 )
              gameexit( " \nThe 'MASTER/First player' just quit the game.  All\nplayers are returned from the game. This only happens in 5-8\nplayer mode as a different network scheme is used.");
      }

      if ((numplayers >= 2) && ((movefifoplc&7) == 7))
      {
            ch = (uint8_t )(randomseed&255);
            for(i=connecthead;i>=0;i=connectpoint2[i])
                 ch += ((ps[i].posx+ps[i].posy+ps[i].posz+ps[i].ang+ps[i].horiz)&255);
            syncval[myconnectindex][syncvalhead[myconnectindex]&(MOVEFIFOSIZ-1)] = ch;
            syncvalhead[myconnectindex]++;


      }

    if(ud.recstat == 1) record();

    if( ud.pause_on == 0 )
    {
        global_random = TRAND;
        movedummyplayers();//ST 13
    }

    for(i=connecthead;i>=0;i=connectpoint2[i])
    {
        cheatkeys(i);

        if( ud.pause_on == 0 )
        {
            processinput(i);
            checksectors(i);
        }
    }

    if( ud.pause_on == 0 )
    {
        movefta();//ST 2
        moveweapons();          //ST 5 (must be last)
        movetransports();       //ST 9

        moveplayers();          //ST 10
        movefallers();          //ST 12
        moveexplosions();       //ST 4

        moveactors();           //ST 1
        moveeffectors();        //ST 3

        movestandables();       //ST 6
        doanimations();
        movefx();               //ST 11
    }

    fakedomovethingscorrect();

    if( (everyothertime&1) == 0)
    {
        animatewalls();
        movecyclers();
        pan3dsound();
    }


    return 0;
}


void doorders(void)
{
    short i;

    setview(0,0,xdim-1,ydim-1);

    for(i=0;i<63;i+=7) palto(0,0,0,i);
    ps[myconnectindex].palette = palette;
    totalclock = 0;
    KB_FlushKeyboardQueue();
    rotatesprite(0,0,65536L,0,ORDERING,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
    totalclock = 0;while( !KB_KeyWaiting() ) getpackets();

    for(i=0;i<63;i+=7) palto(0,0,0,i);
    totalclock = 0;
    KB_FlushKeyboardQueue();
    rotatesprite(0,0,65536L,0,ORDERING+1,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
    totalclock = 0;while( !KB_KeyWaiting() ) getpackets();

    for(i=0;i<63;i+=7) palto(0,0,0,i);
    totalclock = 0;
    KB_FlushKeyboardQueue();
    rotatesprite(0,0,65536L,0,ORDERING+2,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
    totalclock = 0;while( !KB_KeyWaiting() ) getpackets();

    for(i=0;i<63;i+=7) palto(0,0,0,i);
    totalclock = 0;
    KB_FlushKeyboardQueue();
    rotatesprite(0,0,65536L,0,ORDERING+3,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
    totalclock = 0;while( !KB_KeyWaiting() ) getpackets();
}

void dobonus(uint8_t  bonusonly)
{
    short t, gfx_offset;
//    short tinc;
    int32_t i, y,xfragtotal,yfragtotal;
    short bonuscnt;
    char text[512];

    int32_t breathe[] =
    {
         0,  30,VICTORY1+1,176,59,
        30,  60,VICTORY1+2,176,59,
        60,  90,VICTORY1+1,176,59,
        90, 120,0         ,176,59
    };

    int32_t bossmove[] =
    {
         0, 120,VICTORY1+3,86,59,
       220, 260,VICTORY1+4,86,59,
       260, 290,VICTORY1+5,86,59,
       290, 320,VICTORY1+6,86,59,
       320, 350,VICTORY1+7,86,59,
       350, 380,VICTORY1+8,86,59
    };

    bonuscnt = 0;

    for(t=0;t<64;t+=7) palto(0,0,0,t);
    setview(0,0,xdim-1,ydim-1);
    clearview(0L);
    nextpage();
    flushperms();

    FX_StopAllSounds();
    clearsoundlocks();
    FX_SetReverb(0L);

    if(bonusonly) goto FRAGBONUS;

    if(numplayers < 2 && ud.eog && ud.from_bonus == 0)
        switch(ud.volume_number)
    {
        case 0:
            if(ud.lockout == 0)
            {
                clearview(0L);
                rotatesprite(0,50<<16,65536L,0,VICTORY1,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
                nextpage();
                ps[myconnectindex].palette = endingpal;
                for(t=63;t>=0;t--) palto(0,0,0,t);

                KB_FlushKeyboardQueue();
                totalclock = 0; //tinc = 0;
                while( 1 )
                {
                    clearview(0L);
                    rotatesprite(0,50<<16,65536L,0,VICTORY1,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);

                    // boss
                    if( totalclock > 390 && totalclock < 780 )
                        for(t=0;t<35;t+=5) if( bossmove[t+2] && (totalclock%390) > bossmove[t] && (totalclock%390) <= bossmove[t+1] )
                    {
                        if(t==10 && bonuscnt == 1) { sound(SHOTGUN_FIRE);sound(SQUISHED); bonuscnt++; }
                        rotatesprite(bossmove[t+3]<<16,bossmove[t+4]<<16,65536L,0,bossmove[t+2],0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
                    }

                    // Breathe
                    if( totalclock < 450 || totalclock >= 750 )
                    {
                        if(totalclock >= 750)
                        {
                            rotatesprite(86<<16,59<<16,65536L,0,VICTORY1+8,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
                            if(totalclock >= 750 && bonuscnt == 2) { sound(DUKETALKTOBOSS); bonuscnt++; }
                        }
                        for(t=0;t<20;t+=5)
                            if( breathe[t+2] && (totalclock%120) > breathe[t] && (totalclock%120) <= breathe[t+1] )
                        {
                                if(t==5 && bonuscnt == 0)
                                {
                                    sound(BOSSTALKTODUKE);
                                    bonuscnt++;
                                }
                                rotatesprite(breathe[t+3]<<16,breathe[t+4]<<16,65536L,0,breathe[t+2],0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
                        }
                    }

                    getpackets();
                    nextpage();
                    if( KB_KeyWaiting() ) break;
                }
            }

            for(t=0;t<64;t++) palto(0,0,0,t);

            KB_FlushKeyboardQueue();
            ps[myconnectindex].palette = palette;

            rotatesprite(0,0,65536L,0,3292,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
            nextpage(); for(t=63;t>0;t--) palto(0,0,0,t);
            while( !KB_KeyWaiting() ) getpackets();
            for(t=0;t<64;t++) palto(0,0,0,t);
            MUSIC_StopSong();
            FX_StopAllSounds();
            clearsoundlocks();
            break;
        case 1:
            MUSIC_StopSong();
            clearview(0L);
            nextpage();

            if(ud.lockout == 0)
            {
                playanm("cineov2.anm",1);
                KB_FlushKeyboardQueue();
                clearview(0L);
                nextpage();
            }

            sound(PIPEBOMB_EXPLODE);

            for(t=0;t<64;t++) palto(0,0,0,t);
            setview(0,0,xdim-1,ydim-1);
            KB_FlushKeyboardQueue();
            ps[myconnectindex].palette = palette;
            rotatesprite(0,0,65536L,0,3293,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
            nextpage(); for(t=63;t>0;t--) palto(0,0,0,t);
            while( !KB_KeyWaiting() ) getpackets();
            for(t=0;t<64;t++) palto(0,0,0,t);

            break;

        case 3:

            setview(0,0,xdim-1,ydim-1);

            MUSIC_StopSong();
            clearview(0L);
            nextpage();

            if(ud.lockout == 0)
            {
                KB_FlushKeyboardQueue();
                playanm("vol4e1.anm",8);
                clearview(0L);
                nextpage();
                playanm("vol4e2.anm",10);
                clearview(0L);
                nextpage();
                playanm("vol4e3.anm",11);
                clearview(0L);
                nextpage();
            }

            FX_StopAllSounds();
            clearsoundlocks();
            sound(ENDSEQVOL3SND4);
            KB_FlushKeyboardQueue();

            ps[myconnectindex].palette = palette;
            palto(0,0,0,63);
            clearview(0L);
            menutext(160,60,0,0,"THANKS TO ALL OUR");
            menutext(160,60+16,0,0,"FANS FOR GIVING");
            menutext(160,60+16+16,0,0,"US BIG HEADS.");
            menutext(160,70+16+16+16,0,0,"LOOK FOR A DUKE NUKEM 3D");
            menutext(160,70+16+16+16+16,0,0,"SEQUEL SOON.");
            nextpage();

            for(t=63;t>0;t-=3) palto(0,0,0,t);
            KB_FlushKeyboardQueue();
            while(!KB_KeyWaiting()) getpackets();
            for(t=0;t<64;t+=3) palto(0,0,0,t);

            clearview(0L);
            nextpage();

            playanm("DUKETEAM.ANM",4);

            KB_FlushKeyboardQueue();
            while(!KB_KeyWaiting()) getpackets();

            clearview(0L);
            nextpage();
            palto(0,0,0,63);

            FX_StopAllSounds();
            clearsoundlocks();
            KB_FlushKeyboardQueue();

            break;

        case 2:

            MUSIC_StopSong();
            clearview(0L);
            nextpage();
            if(ud.lockout == 0)
            {
                for(t=63;t>=0;t--) palto(0,0,0,t);
                playanm("cineov3.anm",2);
                KB_FlushKeyboardQueue();
                ototalclock = totalclock+200;
                while(totalclock < ototalclock) getpackets();
                clearview(0L);
                nextpage();

                FX_StopAllSounds();
                clearsoundlocks();
            }

            playanm("RADLOGO.ANM",3);

            if( ud.lockout == 0 && !KB_KeyWaiting() )
            {
                sound(ENDSEQVOL3SND5);
                while(Sound[ENDSEQVOL3SND5].lock>=200) getpackets();
                if(KB_KeyWaiting()) goto ENDANM;
                sound(ENDSEQVOL3SND6);
                while(Sound[ENDSEQVOL3SND6].lock>=200) getpackets();
                if(KB_KeyWaiting()) goto ENDANM;
                sound(ENDSEQVOL3SND7);
                while(Sound[ENDSEQVOL3SND7].lock>=200) getpackets();
                if(KB_KeyWaiting()) goto ENDANM;
                sound(ENDSEQVOL3SND8);
                while(Sound[ENDSEQVOL3SND8].lock>=200) getpackets();
                if(KB_KeyWaiting()) goto ENDANM;
                sound(ENDSEQVOL3SND9);
                while(Sound[ENDSEQVOL3SND9].lock>=200) getpackets();
            }

            KB_FlushKeyboardQueue();
            totalclock = 0;
            while(!KB_KeyWaiting() && totalclock < 120) getpackets();

            ENDANM:

            FX_StopAllSounds();
            clearsoundlocks();

            KB_FlushKeyboardQueue();

            clearview(0L);

            break;
    }

    FRAGBONUS:

    ps[myconnectindex].palette = palette;
    KB_FlushKeyboardQueue();
    totalclock = 0; //tinc = 0;
    bonuscnt = 0;

    MUSIC_StopSong();
    FX_StopAllSounds();
    clearsoundlocks();

    if(playerswhenstarted > 1 && ud.coop != 1 )
    {
        if(!(MusicToggle == 0 || MusicDevice == NumSoundCards))
            sound(BONUSMUSIC);

        rotatesprite(0,0,65536L,0,MENUSCREEN,16,0,2+8+16+64,0,0,xdim-1,ydim-1);
        rotatesprite(160<<16,34<<16,65536L,0,INGAMEDUKETHREEDEE,0,0,10,0,0,xdim-1,ydim-1);
        rotatesprite((260)<<16,36<<16,65536L,0,PLUTOPAKSPRITE+2,0,0,2+8,0,0,xdim-1,ydim-1);
        gametext(160,58+2,"MULTIPLAYER TOTALS",0,2+8+16);
        gametext(160,58+10,level_names[(ud.volume_number*11)+ud.last_level-1],0,2+8+16);

        gametext(160,165,"PRESS ANY KEY TO CONTINUE",0,2+8+16);


        t = 0;
        minitext(23,80,"   NAME                                           KILLS",8,2+8+16+128);
        for(i=0;i<playerswhenstarted;i++)
        {
            sprintf(text,"%-4d",i+1);
            minitext(92+(i*23),80,text,3,2+8+16+128);
        }

        for(i=0;i<playerswhenstarted;i++)
        {
            xfragtotal = 0;
            sprintf(text,"%d",i+1);

            minitext(30,90+t,text,0,2+8+16+128);
            minitext(38,90+t,ud.user_name[i],ps[i].palookup,2+8+16+128);

            for(y=0;y<playerswhenstarted;y++)
            {
                if(i == y)
                {
                    sprintf(text,"%-4d",ps[y].fraggedself);
                    minitext(92+(y*23),90+t,text,2,2+8+16+128);
                    xfragtotal -= ps[y].fraggedself;
                }
                else
                {
                    sprintf(text,"%-4d",frags[i][y]);
                    minitext(92+(y*23),90+t,text,0,2+8+16+128);
                    xfragtotal += frags[i][y];
                }

                if(myconnectindex == connecthead)
                {
                    sprintf(text,"stats %d killed %d %d\n",i+1,y+1,frags[i][y]);
                    sendscore(text);
                }
            }

            sprintf(text,"%-4d",xfragtotal);
            minitext(101+(8*23),90+t,text,2,2+8+16+128);

            t += 7;
        }

        for(y=0;y<playerswhenstarted;y++)
        {
            yfragtotal = 0;
            for(i=0;i<playerswhenstarted;i++)
            {
                if(i == y)
                    yfragtotal += ps[i].fraggedself;
                yfragtotal += frags[i][y];
            }
            sprintf(text,"%-4d",yfragtotal);
            minitext(92+(y*23),96+(8*7),text,2,2+8+16+128);
        }

        minitext(45,96+(8*7),"DEATHS",8,2+8+16+128);
        nextpage();

        for(t=0;t<64;t+=7)
            palto(0,0,0,63-t);

        KB_FlushKeyboardQueue();
        while(KB_KeyWaiting()==0) getpackets();

        if( KB_KeyPressed( sc_F12 ) )
        {
            KB_ClearKeyDown( sc_F12 );
            takescreenshot();
        }

        if(bonusonly || ud.multimode > 1) return;

        for(t=0;t<64;t+=7) palto(0,0,0,t);
    }

    if(bonusonly || ud.multimode > 1) return;

    switch(ud.volume_number)
    {
        case 1:
            gfx_offset = 5;
            break;
        default:
            gfx_offset = 0;
            break;
    }

    rotatesprite(0,0,65536L,0,BONUSSCREEN+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);

    menutext(160,20-6,0,0,&level_names[(ud.volume_number*11)+ud.last_level-1][0]);
    menutext(160,36-6,0,0,"COMPLETED");

    gametext(160,192,"PRESS ANY KEY TO CONTINUE",16,2+8+16);

    if(!(MusicToggle == 0 || MusicDevice == NumSoundCards))
        sound(BONUSMUSIC);

    nextpage();
    KB_FlushKeyboardQueue();
    for(t=0;t<64;t++) palto(0,0,0,63-t);
    bonuscnt = 0;
    totalclock = 0; //tinc = 0;

    while( 1 )
    {
                sampletimer();
        if(ps[myconnectindex].gm&MODE_EOL)
        {
            rotatesprite(0,0,65536L,0,BONUSSCREEN+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);

            if( totalclock > (1000000000L) && totalclock < (1000000320L) )
            {
                switch( (totalclock>>4)%15 )
                {
                    case 0:
                        if(bonuscnt == 6)
                        {
                            bonuscnt++;
                            sound(SHOTGUN_COCK);
                            switch(rand()&3)
                            {
                                case 0:
                                    sound(BONUS_SPEECH1);
                                    break;
                                case 1:
                                    sound(BONUS_SPEECH2);
                                    break;
                                case 2:
                                    sound(BONUS_SPEECH3);
                                    break;
                                case 3:
                                    sound(BONUS_SPEECH4);
                                    break;
                            }
                        }
                    case 1:
                    case 4:
                    case 5:
                        rotatesprite(199<<16,31<<16,65536L,0,BONUSSCREEN+3+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
                        break;
                    case 2:
                    case 3:
                       rotatesprite(199<<16,31<<16,65536L,0,BONUSSCREEN+4+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
                       break;
                }
            }
            else if( totalclock > (10240+120L) ) break;
            else
            {
                switch( (totalclock>>5)&3 )
                {
                    case 1:
                    case 3:
                        rotatesprite(199<<16,31<<16,65536L,0,BONUSSCREEN+1+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
                        break;
                    case 2:
                        rotatesprite(199<<16,31<<16,65536L,0,BONUSSCREEN+2+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
                        break;
                }
            }

            menutext(160,20-6,0,0,&level_names[(ud.volume_number*11)+ud.last_level-1][0]);
            menutext(160,36-6,0,0,"COMPLETED");

            gametext(160,192,"PRESS ANY KEY TO CONTINUE",16,2+8+16);

            if( totalclock > (60*3) )
            {
                gametext(10,59+9,"Your Time:",0,2+8+16);
                gametext(10,69+9,"Par time:",0,2+8+16);
                gametext(10,78+9,"3D Realms' Time:",0,2+8+16);
                if(bonuscnt == 0)
                    bonuscnt++;

                if( totalclock > (60*4) )
                {
                    if(bonuscnt == 1)
                    {
                        bonuscnt++;
                        sound(PIPEBOMB_EXPLODE);
                    }
                    sprintf(text,"%02d:%02d",
                        (ps[myconnectindex].player_par/(26*60))%60,
                        (ps[myconnectindex].player_par/26)%60);
                    gametext((320>>2)+71,60+9,text,0,2+8+16);

                    sprintf(text,"%02d:%02d",
                        (partime[ud.volume_number*11+ud.last_level-1]/(26*60))%60,
                        (partime[ud.volume_number*11+ud.last_level-1]/26)%60);
                    gametext((320>>2)+71,69+9,text,0,2+8+16);

                    sprintf(text,"%02d:%02d",
                        (designertime[ud.volume_number*11+ud.last_level-1]/(26*60))%60,
                        (designertime[ud.volume_number*11+ud.last_level-1]/26)%60);
                    gametext((320>>2)+71,78+9,text,0,2+8+16);

                }
            }
            if( totalclock > (60*6) )
            {
                gametext(10,94+9,"Enemies Killed:",0,2+8+16);
                gametext(10,99+4+9,"Enemies Left:",0,2+8+16);

                if(bonuscnt == 2)
                {
                    bonuscnt++;
                    sound(FLY_BY);
                }

                if( totalclock > (60*7) )
                {
                    if(bonuscnt == 3)
                    {
                        bonuscnt++;
                        sound(PIPEBOMB_EXPLODE);
                    }
                    sprintf(text,"%-3hhd",ps[myconnectindex].actors_killed);
                    gametext((320>>2)+70,93+9,text,0,2+8+16);
                    if(ud.player_skill > 3 )
                    {
                        sprintf(text,"N/A");
                        gametext((320>>2)+70,99+4+9,text,0,2+8+16);
                    }
                    else
                    {
                        if( (ps[myconnectindex].max_actors_killed-ps[myconnectindex].actors_killed) < 0 )
                            sprintf(text,"%-3d",0);
                        else sprintf(text,"%-3d",ps[myconnectindex].max_actors_killed-ps[myconnectindex].actors_killed);
                        gametext((320>>2)+70,99+4+9,text,0,2+8+16);
                    }
                }
            }
            if( totalclock > (60*9) )
            {
                gametext(10,120+9,"Secrets Found:",0,2+8+16);
                gametext(10,130+9,"Secrets Missed:",0,2+8+16);
                if(bonuscnt == 4) bonuscnt++;

                if( totalclock > (60*10) )
                {
                    if(bonuscnt == 5)
                    {
                        bonuscnt++;
                        sound(PIPEBOMB_EXPLODE);
                    }
                    sprintf(text,"%-3d",ps[myconnectindex].secret_rooms);
                    gametext((320>>2)+70,120+9,text,0,2+8+16);
                    if( ps[myconnectindex].secret_rooms > 0 )
                        sprintf(text,"%-3d",(100*ps[myconnectindex].secret_rooms/ps[myconnectindex].max_secret_rooms));
                    sprintf(text,"%-3d",ps[myconnectindex].max_secret_rooms-ps[myconnectindex].secret_rooms);
                    gametext((320>>2)+70,130+9,text,0,2+8+16);
                }
            }

            if(totalclock > 10240 && totalclock < 10240+10240)
                totalclock = 1024;

            if( KB_KeyWaiting() && totalclock > (60*2) )
            {
                if( KB_KeyPressed( sc_F12 ) )
                {
                    KB_ClearKeyDown( sc_F12 );
                    takescreenshot();
                }

                if( totalclock < (60*13) )
                {
                    KB_FlushKeyboardQueue();
                    totalclock = (60*13);
                }
                else if( totalclock < (1000000000L))
                   totalclock = (1000000000L);
            }
        }
        else break;
        nextpage();
    }
}


void cameratext(short i)
{
    uint8_t  flipbits;
    int32_t x , y;

    if(!T1)
    {
        rotatesprite(24<<16,33<<16,65536L,0,CAMCORNER,0,0,2,windowx1,windowy1,windowx2,windowy2);
        rotatesprite((320-26)<<16,34<<16,65536L,0,CAMCORNER+1,0,0,2,windowx1,windowy1,windowx2,windowy2);
        rotatesprite(22<<16,163<<16,65536L,512,CAMCORNER+1,0,0,2+4,windowx1,windowy1,windowx2,windowy2);
        rotatesprite((310-10)<<16,163<<16,65536L,512,CAMCORNER+1,0,0,2,windowx1,windowy1,windowx2,windowy2);
        if(totalclock&16)
            rotatesprite(46<<16,32<<16,65536L,0,CAMLIGHT,0,0,2,windowx1,windowy1,windowx2,windowy2);
    }
    else
    {
        flipbits = (totalclock<<1)&48;
        for(x=0;x<394;x+=64)
            for(y=0;y<200;y+=64)
                rotatesprite(x<<16,y<<16,65536L,0,STATIC,0,0,2+flipbits,windowx1,windowy1,windowx2,windowy2);
    }
}

void vglass(int32_t x,int32_t y,short a,short wn,short n)
{
    int32_t z, zincs;
    short sect;

    sect = wall[wn].nextsector;
    if(sect == -1) return;
    zincs = ( sector[sect].floorz-sector[sect].ceilingz ) / n;

    for(z = sector[sect].ceilingz;z < sector[sect].floorz; z += zincs )
        EGS(sect,x,y,z-(TRAND&8191),GLASSPIECES+(z&(TRAND%3)),-32,36,36,a+128-(TRAND&255),16+(TRAND&31),0,-1,5);
}

void lotsofglass(short i,short wallnum,short n)
{
     int32_t j, xv, yv, z, x1, y1;
     short sect, a;

     sect = -1;

     if(wallnum < 0)
     {
        for(j=n-1; j >= 0 ;j--)
        {
            a = SA-256+(TRAND&511)+1024;
            EGS(SECT,SX,SY,SZ,GLASSPIECES+(j%3),-32,36,36,a,32+(TRAND&63),1024-(TRAND&1023),i,5);
        }
        return;
     }

     j = n+1;

     x1 = wall[wallnum].x;
     y1 = wall[wallnum].y;

     xv = wall[wall[wallnum].point2].x-x1;
     yv = wall[wall[wallnum].point2].y-y1;

     x1 -= ksgn(yv);
     y1 += ksgn(xv);

     xv /= j;
     yv /= j;

     for(j=n;j>0;j--)
         {
                  x1 += xv;
                  y1 += yv;

          updatesector(x1,y1,&sect);
          if(sect >= 0)
          {
              z = sector[sect].floorz-(TRAND&(klabs(sector[sect].ceilingz-sector[sect].floorz)));
              if( z < -(32<<8) || z > (32<<8) )
                  z = SZ-(32<<8)+(TRAND&((64<<8)-1));
              a = SA-1024;
              EGS(SECT,x1,y1,z,GLASSPIECES+(j%3),-32,36,36,a,32+(TRAND&63),-(TRAND&1023),i,5);
          }
         }
}

void spriteglass(short i,short n)
{
    int32_t j, k, a, z;

    for(j=n;j>0;j--)
    {
        a = TRAND&2047;
        z = SZ-((TRAND&16)<<8);
        k = EGS(SECT,SX,SY,z,GLASSPIECES+(j%3),TRAND&15,36,36,a,32+(TRAND&63),-512-(TRAND&2047),i,5);
        sprite[k].pal = sprite[i].pal;
    }
}

void ceilingglass(short i,short sectnum,short n)
{
     int32_t j, xv, yv, z, x1, y1;
     short a,s, startwall,endwall;

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

     for(s=startwall;s<(endwall-1);s++)
     {
         x1 = wall[s].x;
         y1 = wall[s].y;

         xv = (wall[s+1].x-x1)/(n+1);
         yv = (wall[s+1].y-y1)/(n+1);

         for(j=n;j>0;j--)
         {
              x1 += xv;
              y1 += yv;
              a = TRAND&2047;
              z = sector[sectnum].ceilingz+((TRAND&15)<<8);
              EGS(sectnum,x1,y1,z,GLASSPIECES+(j%3),-32,36,36,a,(TRAND&31),0,i,5);
          }
     }
}



void lotsofcolourglass(short i,short wallnum,short n)
{
     int32_t j, xv, yv, z, x1, y1;
     short sect = -1, a, k;

     if(wallnum < 0)
     {
        for(j=n-1; j >= 0 ;j--)
        {
            a = TRAND&2047;
            k = EGS(SECT,SX,SY,SZ-(TRAND&(63<<8)),GLASSPIECES+(j%3),-32,36,36,a,32+(TRAND&63),1024-(TRAND&2047),i,5);
            sprite[k].pal = TRAND&15;
        }
        return;
     }

     j = n+1;
     x1 = wall[wallnum].x;
     y1 = wall[wallnum].y;

     xv = (wall[wall[wallnum].point2].x-wall[wallnum].x)/j;
     yv = (wall[wall[wallnum].point2].y-wall[wallnum].y)/j;

     for(j=n;j>0;j--)
         {
                  x1 += xv;
                  y1 += yv;

          updatesector(x1,y1,&sect);
          z = sector[sect].floorz-(TRAND&(klabs(sector[sect].ceilingz-sector[sect].floorz)));
          if( z < -(32<<8) || z > (32<<8) )
              z = SZ-(32<<8)+(TRAND&((64<<8)-1));
          a = SA-1024;
          k = EGS(SECT,x1,y1,z,GLASSPIECES+(j%3),-32,36,36,a,32+(TRAND&63),-(TRAND&2047),i,5);
          sprite[k].pal = TRAND&7;
         }
}

void SetupGameButtons( void )
{
   CONTROL_DefineFlag(gamefunc_Move_Forward,false);
   CONTROL_DefineFlag(gamefunc_Move_Backward,false);
   CONTROL_DefineFlag(gamefunc_Turn_Left,false);
   CONTROL_DefineFlag(gamefunc_Turn_Right,false);
   CONTROL_DefineFlag(gamefunc_Strafe,false);
   CONTROL_DefineFlag(gamefunc_Fire,false);
   CONTROL_DefineFlag(gamefunc_Open,false);
   CONTROL_DefineFlag(gamefunc_Run,false);
   CONTROL_DefineFlag(gamefunc_AutoRun,false);
   CONTROL_DefineFlag(gamefunc_Jump,false);
   CONTROL_DefineFlag(gamefunc_Crouch,false);
   CONTROL_DefineFlag(gamefunc_Look_Up,false);
   CONTROL_DefineFlag(gamefunc_Look_Down,false);
   CONTROL_DefineFlag(gamefunc_Look_Left,false);
   CONTROL_DefineFlag(gamefunc_Look_Right,false);
   CONTROL_DefineFlag(gamefunc_Strafe_Left,false);
   CONTROL_DefineFlag(gamefunc_Strafe_Right,false);
   CONTROL_DefineFlag(gamefunc_Aim_Up,false);
   CONTROL_DefineFlag(gamefunc_Aim_Down,false);
   CONTROL_DefineFlag(gamefunc_Weapon_1,false);
   CONTROL_DefineFlag(gamefunc_Weapon_2,false);
   CONTROL_DefineFlag(gamefunc_Weapon_3,false);
   CONTROL_DefineFlag(gamefunc_Weapon_4,false);
   CONTROL_DefineFlag(gamefunc_Weapon_5,false);
   CONTROL_DefineFlag(gamefunc_Weapon_6,false);
   CONTROL_DefineFlag(gamefunc_Weapon_7,false);
   CONTROL_DefineFlag(gamefunc_Weapon_8,false);
   CONTROL_DefineFlag(gamefunc_Weapon_9,false);
   CONTROL_DefineFlag(gamefunc_Weapon_10,false);
   CONTROL_DefineFlag(gamefunc_Inventory,false);
   CONTROL_DefineFlag(gamefunc_Inventory_Left,false);
   CONTROL_DefineFlag(gamefunc_Inventory_Right,false);
   CONTROL_DefineFlag(gamefunc_Holo_Duke,false);
   CONTROL_DefineFlag(gamefunc_Jetpack,false);
   CONTROL_DefineFlag(gamefunc_NightVision,false);
   CONTROL_DefineFlag(gamefunc_MedKit,false);
   CONTROL_DefineFlag(gamefunc_TurnAround,false);
   CONTROL_DefineFlag(gamefunc_SendMessage,false);
   CONTROL_DefineFlag(gamefunc_Map,false);
   CONTROL_DefineFlag(gamefunc_Shrink_Screen,false);
   CONTROL_DefineFlag(gamefunc_Enlarge_Screen,false);
   CONTROL_DefineFlag(gamefunc_Center_View,false);
   CONTROL_DefineFlag(gamefunc_Holster_Weapon,false);
   CONTROL_DefineFlag(gamefunc_Show_Opponents_Weapon,false);
   CONTROL_DefineFlag(gamefunc_Map_Follow_Mode,false);
   CONTROL_DefineFlag(gamefunc_See_Coop_View,false);
   CONTROL_DefineFlag(gamefunc_Mouse_Aiming,false);
   CONTROL_DefineFlag(gamefunc_Toggle_Crosshair,false);
   CONTROL_DefineFlag(gamefunc_Steroids,false);
   CONTROL_DefineFlag(gamefunc_Quick_Kick,false);
   CONTROL_DefineFlag(gamefunc_Next_Weapon,false);
   CONTROL_DefineFlag(gamefunc_Previous_Weapon,false);
   CONTROL_DefineFlag(gamefunc_Console,false);
}

/*
===================
=
= GetTime
=
===================
*/

int32_t GetTime(void)
   {
   return totalclock;
   }


/*
===================
=
= CenterCenter
=
===================
*/

void CenterCenter(void)
   {
   printf("Center the joystick and press a button\n");
   }

/*
===================
=
= UpperLeft
=
===================
*/

void UpperLeft(void)
   {
   printf("Move joystick to upper-left corner and press a button\n");
   }

/*
===================
=
= LowerRight
=
===================
*/

void LowerRight(void)
   {
   printf("Move joystick to lower-right corner and press a button\n");
   }

/*
===================
=
= CenterThrottle
=
===================
*/

void CenterThrottle(void)
   {
   printf("Center the throttle control and press a button\n");
   }

/*
===================
=
= CenterRudder
=
===================
*/

void CenterRudder(void)
{
   printf("Center the rudder control and press a button\n");
}

// FIX_00006: better naming system for screenshots + message when pic is taken.
//            Use ./screenshots folder. Screenshot code rerwritten. Faster and
//            makes smaller files. Doesn't freeze or lag the game anymore.
void takescreenshot(void)
{
    char  szFilename[256];
    int i;
    char  score[20];
    time_t time4file;
    char text[512];

    // xduke: Build a nice name w/ date and players name if in multi mode.
    time(&time4file);

    sprintf(text, "Chocolate DukeNukem3D(v%d.%d) %d",
            CHOCOLATE_DUKE_REV_X,
            CHOCOLATE_DUKE_REV_DOT_Y,
            time(NULL));

    if(ud.multimode>1) // if more than 1 player, we add name. Then add score if DM
    {
        text[0] = '\0';
        strcat((char *)text, " [");
        for(i=connecthead;i>=0;i=connectpoint2[i])
        {
            if(!ud.user_name[i][0])
                strcat(text, "NoName");
            else
                strcat(text, &ud.user_name[i][0]);

            if(ud.m_coop==0 || ud.m_coop==2)  // if DM or DM No spawn. Add Score as well
            {
                strcat(text, "(");
                snprintf(score, sizeof(score), "%d",ps[i].frag-ps[i].fraggedself);
                strcat(text, score);
                strcat(text, ") vs ");
            }
            else
                strcat(text, " vs ");
        }
        tempbuf[strlen(text)-4]=0; // remove last vs
        strcat(text, "]");
    }
    strcat(text, ".bmp");


    // If this is a TC save it to the TC's directory
    if(getGameDir()[0] != '\0')
    {
        sprintf(szFilename, "%s\\%s", getGameDir(), SCREENSHOTPATH);
        mkdir(szFilename);
        sprintf(szFilename, "%s\\%s\\%s", getGameDir(), SCREENSHOTPATH, tempbuf);
    }
    // otherwise let's save it to the root.
    else
    {
        mkdir(SCREENSHOTPATH);
        sprintf(szFilename, "%s\\%s", SCREENSHOTPATH, tempbuf);
    }

    if(SafeFileExists(szFilename) == 0)
    {
        screencapture(szFilename,0);
        sprintf(fta_quotes[103],"SCREEN SAVED");
        sound(EXITMENUSOUND);
    }
    else
        sprintf(fta_quotes[103],"CAN'T WRITE FILE!");

    FTA(103,&ps[screenpeek],1);

}


// Rare Multiplayer, when dead, total screen screwup back again!
// E3l1 (Coop /w monsters) sprite list corrupt 50%
// Univbe exit, instead, default to screen buffer.
// Check all caches bounds and memory usages
// Fix enlarger weapon selections to perfection
// Need sounds.c
// Spawning a couple of sounds at the same time
// Check Weapon Switching
// FIRE and FIRE2
// Where should I flash the screen white???
// Jittery on subs in mp?
// Check accurate memory amounts!
// Why squish sound at hit space when dead?
// Falling Counter Not reset in mp
// Wierd small freezer
// Double freeze on player?, still firing
// Do Mouse Flip option
// Save mouse aiming
// Laser bounce off mirrors
// GEORGE:   Ten in text screen.
// Alien:
// Freeze: change
// Press space holding player
// Press space
// tank broke
// 2d mode fucked in fake mp mode
// 207
// Mail not rolling up on conveyers
// Fix all alien animations
// Do episode names in .CONS
// do syntak check for "{?????"
// Make commline parms set approiate multiplayer flags

// Check all breakables to see if they are exploding properly
// Fix freezing palette on Alien

// Do a demo make run overnite
// Fix Super Duck
// Slime Guies, use quickkick.

// Make Lasers from trip bombs reflect off mirrors
// Remember for lockout of sound swears
// Pass sender in packed, NOT
// Fatal sync give no message for TEN
// Hitting TEN BUTTON(OPTION) no TEN SCreen
// Check multioperateswitches for se 31,32
// Fix pal for ceilings (SE#18)
// case 31: sprites up one high
// E1l1 No Kill All troops in room, sleep time

// Fifo for message list

// Bloodsplat on conveyers

// Meclanical
// Increase sound
// Mouse Delay at death
// Wierd slowdown

// Footprints on stuff floating

// Ken, The inside function is called a lot in -1 sectors
// No loading Univbe message rewrite
// Expander must cycle with rest of weapons
// Duck SHOOT PIPEBOMB, red wall

// Get commit source from mark

/*
  1. fix pipebomb bug
  2. check george maps
  4. Save/Restore check (MP and SP)
  5. Check TEN
  6. Get Commit fixed
  8. Is mail slow?
  9. Cacheing
  10. Blue out "PLAY ON TEN" in MULTIPLAYER
  11. Eight Player test
  12. Postal.voc not found.
  13. All Monsters explode in arcade,
  check SEENINE STRENGTH,
  Change 28<<8 back to 16<<8 in hitradius
  Compare 1.3d to 1.4
  14. Check sounds/gfx for for parr lock
  15. Player # Loaded a game
  16. Replace Crane code 1.3d to 1.4
  17. Fix Greenslime
  18. Small Freeze sprite,below floor
  19. Vesa message auto abort in mp?
  20. Fucked Palette in my skip ahead in MP
  21. Load in main menu
  22. Rotated frag screen no game screen
  23. Jibs sounds when killed other dukes
  24. Ten code and /f4 mode
  25. Fix All MP Glitches!!
  26. Unrem Menues anim tenbn
  27. buy groc,clothes,scanner
  28. Why Double Defs in global and game, is so at work
  29. Check that all .objs are erased
  30. Check why 1.3ds gotweapon gamedef coop code no workie
  31. Heavy mods to net code
  32. Make sure all commline stuff works,
  33. killed all waitfor???
  34. 90k stack
  35. double door probs
  36: copy protection
  * when you start a game the duke saying that is played when you choose a skill the sound is cut off.
  * NEWBEASTJUMPING is not deleted at premap in multi-play
  if(*c == '4') no work need objs ask ken, commit
  {
  movesperpacket = 4;
  setpackettimeout(0x3fffffff,0x3fffffff);
  }
  remember, netcode load
*/
//  Ai Problem in god mode.
// Checkplayerhurtwall for forcefields bigforce
// Nuddie, posters. IMF
// Release commit.c to public?
// Document Save bug with mp
// Check moves per packet /f4 waitforeverybody over net?
// Kill IDF OBJ
// No shotguns under water @ tanker
// Unrem copyprotect
// Look for printf and puts
// Check con rewrites
// erase mmulti.c, or get newest objs
// Why nomonsters screwy in load menu in mp
// load last > 'y' == NOT
// Check xptr oos when dead rising to surface.
//    diaginal warping with shotguns
// Test white room.  Lasertripbomb arming crash
// The Bog
// Run Duke Out of windows
// Put Version number in con files
// Test diff. version playing together
// Reorganize dukecd
// Put out patch w/ two weeks testing
// Print draw3d
// Double Klick

/*
  Duke Nukem V

  Layout:

  Settings:
  Suburbs
  Duke inflitrating neighborhoods inf. by aliens
  Death Valley:
  Sorta like a western.  Bull-skulls half buried in the sand
  Military compound:  Aliens take over nuke-missle silo, duke
  must destroy.
  Abondend Aircraft field
  Vegas:
  Blast anything bright!  Alien lights camoflauged.
  Alien Drug factory. The Blue Liquid
  Mountainal Cave:
  Interior cave battles.
  Jungle:
  Trees, canopee, animals, a mysterious hole in the earth with
  gas seaping thru.
  Penetencury:
  Good use of spotlights:
  Mental ward:
  People whom have claimed to be slowly changing into an
  alien species

  Inventory:
  Wood,
  Metal,
  Torch,
  Rope,
  Plastique,
  Cloth,
  Wiring,
  Glue,
  Cigars,
  Food,
  Duck Tape,
  Nails,
  Piping,
  Petrol,
  Uranium,
  Gold,
  Prism,
  Power Cell,

  Hand spikes (Limited usage, they become dull)
  Oxygent     (Oxygen mixed with stimulant)


  Player Skills:
  R-Left,R-Right,Foward,Back
  Strafe, Jump, Double Flip Jump for distance
  Help, Escape
  Fire/Use
  Use Menu

  After a brief resbit, Duke decides to get back to work.

  Cmdr:   "Duke, we've got a lot of scared people down there.
  Some reports even claim that people are already
  slowly changing into aliens."
  Duke:   "No problem, my speciality is in croud control."
  Cmdr:   "Croud control, my ass!  Remember that incident
  during the war?  You created nuthin' but death and
  destruction."
  Duke:   "Not destruction, justice."
  Cmdr:   "I'll take no responsibility for your actions.  Your on
  your own!  Behave your self, damnit!  You got that,
  soldger?"
  Duke:   "I've always been on my own...   Face it, it's ass kickin' time,
  SIR!"
  Cmdr:   "Get outta here...!"
  (Duke gives the Cmdr a hard stair, then cocks his weapon and
  walks out of the room)
  Cmdr:   In a wisper: "Good luck, my friend."

  (Cut to a scene where aliens are injecting genetic material
  into an unconcious subject)

  Programming:   ( the functions I need )
  Images: Polys
  Actors:
  Multi-Object sections for change (head,arms,legs,torsoe,all change)
  Facial expressions.  Pal lookup per poly?

  struct imagetype
  {
  int *itable; // AngX,AngY,AngZ,Xoff,Yoff,Zoff;
  int *idata;
  struct imagetype *prev, *next;
  }

*/


// Test frag screen name fuckup
// Test all xptrs
// Make Jibs stick to ceiling
// Save Game menu crash
// Cache len sum err
// Loading in main (MP), reset totalclock?
// White Room
// Sound hitch with repeat bits
// Rewrite saved menues so no crash
// Put a getpackets after loadplayer in menus
// Put "loading..." before waitfor in loadpla
// No ready2send = 0 for loading
// Test Joystick
// Ten
// Bog
// Test Blimp respawn
// move 1 in player???
