the cutting room mirror

Uncompiled Source Code

This is a sub-page of Zoop (DOS).

ZOOP.OLD is the game's main C++ source code file.

/*
    HOOKSTONE

    PROJECT    : Zoop
    MODULE     : Main
    AUTHOR     : J.Johnson  Advanced Visual Productions

    <Notes>

    Inertia for dial.

    Antialias edges for title screens.

    Bugs as per viacom bug report.

    <Bugs>

    Managed to get a WIN when escape pressed.

    When everybody dies and you start again, the level & blocks are wrong.

    Perhaps I need to flush messages.

    When a players dies, his name can still be seen at bottom of other screens.

    More than one person could win.

    Some workstations are not seeing other players.

    Does not seem to addnodes/deletenodes correctly. New players join but possibly
    at list position >4 so they dont actually get seen on some workstations.

*/

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <i86.h>
#include <time.h>
#include <graph.h>

#include "xtypes.h"
#include "drivers.hpp"
#include "svga.hpp"
#include "pcx.hpp"
#include "gfxcomp8.hpp"
#include "keydefs.hpp"

//Univbe
#include "uvbelib.h"

// Sound library
#include "ail.h"
#include "sound.hpp"

//Network library
#include "zoopnet.hpp"

//===========================================================================
// Definitions
//===========================================================================

#define MAXLEVELS  10

#define MAPWIDTH   40
#define MAPHEIGHT  30
#define MAPSIZE    MAPWIDTH*MAPHEIGHT

// NUMSPRITES is max sprites - (4*5)*2 vert (8*4)*2 horz
// +1 for main man +1 for enemy win if full +32 for fx
// +6 for score +5 for bonus +4 for kills +24 for net
#define NUMSPRITES 200

// Player I/O Control

#define CONTROL_JOYSTICK    0
#define CONTROL_MOUSE       1
#define CONTROL_KEYBOARD    2

#define MOUSE_BUTTON_RIGHT    0xffff-1
#define MOUSE_BUTTON_LEFT     0xffff-2

// Player modes

#define INMIDDLE  0
#define RUNTO     1
#define RUNFROM   2

#define FACEUP    0
#define FACEDOWN  1
#define FACELEFT  2
#define FACERIGHT 3

#define FIRESTEP  32

// Animation defines

#define ADOWN       0
#define ARIGHT      1
#define AUP         2
#define ALEFT     3
#define AWDOWN    4
#define AWRIGHT   5
#define AWUP      6
#define AWLEFT    7

// Quadrant id's

#define QUADTOP   1
#define QUADBOT   2
#define QUADLEFT  3
#define QUADRIGHT 4

#define NUMLINES  16

// Game Type
#define CONTINUE    0
#define LEVELPLAY   1
#define MULTI       2

// Holdback time for full line

#define HOLDBACKTIME        (24*16)

// Network

#define NET_Back_Color          237

//===========================================================================
// External declarations
//===========================================================================

extern "C" {
    void BlitComp8Sprite32x32(UBYTE *,UBYTE *);
    void BlitComp8SSprite32x32(UBYTE *,UBYTE *);
};

//===========================================================================
// Structure definitions
//===========================================================================

struct TUPDATE
{
    UWORD               x, y;
    UDWORD              offset;
    UDWORD              bank1, bank2;
    UBYTE               *start1, *start2;
    UWORD               phase;
    UBYTE               *buftop1, *bufbot1;
    UBYTE               *buftop2, *bufbot2;
    UWORD               lines1, lines2;
    BOOL                update1, update2;
};

//---------------------------------------------------------------------------
// Include Zoop sprite structure and frame definitions
//---------------------------------------------------------------------------

#include "zoopspr.h"

//===========================================================================
// Level definition
//===========================================================================

#define BMNONE    0
#define BM1         1
#define BM2         2
#define BM3         4

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

struct LEVEL
{
    DWORD               Speed;
    WORD                ColorThreshold;
    WORD                Kills;
    UBYTE               WhichBombs;
    WORD                Bombs;
    UWORD               BonusRand;
    BYTE                BackImage[12];
    UWORD               StrobeColor1;
    UWORD               StrobeColor2;
};

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

LEVEL LevelCon[MAXLEVELS] = {
/*   Spd   ColTh Kills  Bombs             BombNo Bonus  Image               Strb1    Strb2
    ------ ------ ----- ------------------ ------ -----  ------------------  -------  ----- */
    { 45,       35, 20, BMNONE,             0,     10,      "scr1.imx",          112,        112  } ,
    { 40,       35, 50, BMNONE,             0,     12,      "scr2.imx",          113,        113  } ,
    { 35,       35, 90, BM1,                   2,        12,        "scr3.imx",          114,        114  } ,
    { 30,       35, 100,    BM2,                   2,        14,        "scr4.imx",          115,        115  } ,
    { 25,       35, 110,    BM3,                   2,        14,        "scr5.imx",          116,        116  } ,
    { 20,       35, 120,    BM1+BM2,               2,        16,        "scr6.imx",          117,        117  } ,
    { 17,       35, 110,    BM2+BM3,               2,        16,        "scr7.imx",          118,        118  } ,
    { 14,       35, 90, BM1+BM2+BM3,       2,        18,        "scr8.imx",          119,        120  } ,
    { 11,       35, 80, BM1+BM2+BM3,        3,       18,        "scr9.imx",          254,        254  } ,
    { 4,        35, 70, BM1+BM2+BM3,        3,       20,        "scr10.imx",         254,        254  }
};

UWORD DifficultyCon[5] = { 0, 30, 39, 47, 53 };

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

LEVEL LevelLev[MAXLEVELS] = {
/*   Spd   ColTh Kills  Bombs             BombNo Bonus  Image               Strb1    Strb2
    ------ ------ ----- ------------------ ------ -----  ------------------  -------  ----- */
    { 45,       35, 20, BMNONE,             0,     10,      "scr1.imx",          112,        112  } ,
    { 40,       35, 50, BMNONE,             0,     12,      "scr2.imx",          113,        113  } ,
    { 35,       35, 90, BM1,                   2,        12,        "scr3.imx",          114,        114  } ,
    { 30,       35, 100,    BM2,                   2,        14,        "scr4.imx",          115,        115  } ,
    { 25,       35, 110,    BM3,                   2,        14,        "scr5.imx",          116,        116  } ,
    { 20,       35, 120,    BM1+BM2,               2,        16,        "scr6.imx",          117,        117  } ,
    { 17,       35, 140,    BM2+BM3,               2,        16,        "scr7.imx",          118,        118  } ,
    { 14,       35, 160,    BM1+BM2+BM3,       2,        18,        "scr8.imx",          119,        120  } ,
    { 11,       35, 180,    BM1+BM2+BM3,        3,       18,        "scr9.imx",          254,        254  } ,
    { 10,       35, 200,    BM1+BM2+BM3,        3,       20,        "scr10.imx",         254,        254  }
};

UWORD DifficultyLev[5] = { 0, 14, 20, 25, 30 };

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

LEVEL LevelNet[MAXLEVELS] = {
/*   Spd   ColTh Kills  Bombs             BombNo Bonus  Image               Strb1    Strb2
    ------ ------ ----- ------------------ ------ -----  ------------------  -------  ----- */
    { 45,       35, 20, BMNONE,             0,     50,      "scr1.imx",          112,        112  } ,
    { 40,       35, 50, BMNONE,             0,     52,      "scr2.imx",          113,        113  } ,
    { 35,       35, 90, BM1,                   2,        52,        "scr3.imx",          114,        114  } ,
    { 30,       35, 100,    BM2,                   2,        54,        "scr4.imx",          115,        115  } ,
    { 25,       35, 110,    BM3,                   2,        54,        "scr5.imx",          116,        116  } ,
    { 20,       35, 120,    BM1+BM2,               2,        56,        "scr6.imx",          117,        117  } ,
    { 17,       35, 110,    BM2+BM3,               2,        56,        "scr7.imx",          118,        118  } ,
    { 14,       35, 90, BM1+BM2+BM3,       2,        60,        "scr8.imx",          119,        120  } ,
    { 11,       35, 80, BM1+BM2+BM3,        3,       60,        "scr9.imx",          254,        254  } ,
    { 4,        35, 70, BM1+BM2+BM3,        3,       70,        "scr10.imx",         254,        254  }
};

UWORD DifficultyNet[5] = { 0, 30, 39, 47, 53 };

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

LEVEL *LevelSet;
UWORD *DifficultySet;

//===========================================================================
// Line definition
//===========================================================================

struct LINE
{
    UWORD               backcolor;      //color of last sprite in line
    UWORD               count;         //count of sprites on line
    UWORD               killcount;     //count of killer sprites on line
    UWORD               wait;          //wait value for line
    UWORD               quad;          //line quad
};

//===========================================================================
// Dpmi memory info
//===========================================================================

struct meminfo
{
  DWORD                     LargestBlockAvail;
  DWORD                     MaxUnlockedPage;
  DWORD                     LargestLockablePage;
  DWORD                     LinAddrSpace;
  DWORD                     NumFreePagesAvail;
  DWORD                     NumPhysicalPagesFree;
  DWORD                     TotalPhysicalPages;
  DWORD                     FreeLinAddrSpace;
  DWORD                     SizeOfPageFile;
  DWORD                     Reserved[3];
} MemInfo;

#define DPMI_INT  0x31

//===========================================================================
// Object/Structure instances & real machine variable declaration
//===========================================================================

JOYSTICK Joy;
UDWORD JoyNormalx,JoyNormaly;

PCXIMAGE BackPcx;
PCXIMAGE GOverPcx;

SPRITE GSprite[NUMSPRITES];

GFX GFXCas;

LINE GLine[NUMLINES];
BOOL DangerLine[NUMLINES];
WORD GLastQuad;

// Graphic image and animation

OBJECT *GOBJ;
UWORD  *GFRM;
UBYTE  **GDAT;
ANIM   **GANM;

UWORD Score[7];
UWORD Digit[7],ODigit[7];
UWORD SKill[4];
UWORD KDigit[4],KODigit[4];
UWORD SLevel[2];
UWORD LDigit[2],LODigit[2];

// Game control

UDWORD AuxInput;

UBYTE Key_Down;
UBYTE Key_Up;
UBYTE Key_Left;
UBYTE Key_Right;
UBYTE Key_Fire;
UBYTE Key_Pause;

// Alter TimeSpeed and GColorThreshold to increase difficulty

UWORD BonusRand;
WORD  BonusCount;
WORD  SpringFull;
UWORD Spring[6];
UWORD SpringMerge[6];

BYTE HiTime[8];

UWORD GColorThreshold;
DWORD TimeSpeed;
WORD  PlayerSpeed,PlayerMoveBy;
UWORD GPlayerMode,GPlayerFacing;
WORD  GPlayerRunLine;
UWORD GStartRunx,GStartRuny,LastLineKill;
UDWORD GScore,GBScore,Accum;
WORD  WalkLine,LastKill,NumBombs,LastBombQuad,LastBombKill;
BOOL  BombTwoFired;
UWORD FireWait;
BOOL  FireEnableStatus;
BOOL  GGameOver;
BOOL    DoingLeadIn;

UBYTE GPalette[768];
UBYTE APalette[768];
UBYTE WPalette[768];
UBYTE TPalette[768];

#define BEING_BUILT 0
#define BUILT           1
#define WILL_KILL       2
DWORD LineState;

WORD  Level,OldLevel,Kills,LevelBombs;
WORD  EnemyLeadIn;
WORD  GameMode;
UDWORD TotalKills,GBKills;
UBYTE WhichBombs;
BOOL DoBeam;
BOOL Strobe;
BYTE StrobeVal1,StrobeVal2,StrobeColor1,StrobeColor2;
BYTE StrobeR1,StrobeR2;
BYTE StrobeG1,StrobeG2;
BYTE StrobeB1,StrobeB2;
WORD StrobeSound;
BOOL Bomb2Sound;

UWORD DetailLevel;

WORD NEWxpos,NEWypos;
WORD NEWrquad,NEWquadline;
WORD NEWrcolor,NEWspr;

WORD MOVquadline;
WORD MOVmx,MOVmy;
WORD MOVcx,MOVcy;
WORD MOVanm;

WORD AppearCount,HoldBack,HoldLine;

TUPDATE MAPUpdate[MAPSIZE+MAPWIDTH];

UBYTE buf1[307200];  //graphic update buffer
UBYTE buf2[307200];  //background buffer
UBYTE buf3[307200];  //transition buffer

UWORD bankstart[5];
UDWORD Yoffset[480];
UDWORD Moffset[MAPHEIGHT];

SVGA Svga;

//===========================================================================
// Network
//===========================================================================

BOOL  NET_Installed;
DWORD NET_Game;

BYTE  NET_Name_Letter[20];
DWORD NET_Name_Sprite[20];
DWORD NET_Name_Posx[20];

BOOL    NET_Logged_In;

//===========================================================================
// Event variables
//===========================================================================

// AddBlocks event

DWORD EVENT_AddBlock;
DWORD BlockAddAmount, BlockAddColor;

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

// Bonus event

DWORD EVENT_Bonus;
DWORD EVENT_BlowUp;

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

// Speedup event

DWORD EVENT_SpeedUp;
DWORD SpeedUpDuration, OldSpeed;

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

BOOL EVENT_AddTarget;

// Pause event

BOOL EVENT_Pause;

//---------------------------------------------------------------------------
// Weapon send state variables
//---------------------------------------------------------------------------

DWORD       NETSEQUENCE;
DWORD       NETSEQUENCECOUNT;
DWORD       NETTARGETTYPE;

//===========================================================================
// Globals for setup selections
//===========================================================================

UWORD Difficulty;
UWORD LastDifficulty;
WORD LastLevel;

//===========================================================================
// DPMI Memory available
//===========================================================================

BYTE MemFree[12];

void MemAvail()
{
  BYTE bitinfo[30];
  BYTE tmp[10];

  union REGS regs;
  struct SREGS sregs;

  regs.x.eax = 0x00000500;
  memset( &sregs, 0, sizeof(sregs) );
  sregs.es = FP_SEG( &MemInfo );
  regs.x.edi = FP_OFF( &MemInfo );

  int386x( DPMI_INT, &regs, &regs, &sregs );

  MemFree[0]=0;
  ltoa(MemInfo.LargestBlockAvail/1024,MemFree,10);
  strcat(MemFree,"K");
}

//===========================================================================
// Inline assembler
//===========================================================================

extern void BUFBlitTile32x32(UBYTE *,UBYTE *,DWORD);
#pragma aux BUFBlitTile32x32 = \
                                "           mov ecx,ebx"\
                                "           mov esi,eax"\
                                "           mov edi,edx"\
                                "           jmp dotileblit"\
                                "dotileblit:"\
                                "           mov eax,[esi]"\
                                "           mov [edi],eax"\
                                "           mov eax,[esi+4]"\
                                "           mov [edi+4],eax"\
                                "           mov eax,[esi+8]"\
                                "           mov [edi+8],eax"\
                                "           mov eax,[esi+12]"\
                                "           mov [edi+12],eax"\
                                "           add esi,640"\
                                "           add edi,640"\
                                "           dec ecx"\
                                "           jnz dotileblit"\
parm caller [eax] [edx] [ebx] modify [eax esi edi ecx];

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

// Set palette DAC

extern void PalDac(DWORD,DWORD);
#pragma aux PalDac = \
                                "           mov   ebx, edx"\
                                "           mov dx, 03c8h"\
                                "           out dx, al"\
                                "           inc dx"\
                                "        mov   eax,ebx"\
                                "           out dx, al"\
                                "           shr   eax, 8"\
                                "           out dx, al"\
                                "           shr   eax, 8"\
                                "           out dx, al"\
parm caller [eax] [edx] modify [eax ebx edx];

//===========================================================================
// Timer functions
//===========================================================================

volatile UDWORD TimerCount;
HTIMER GameTimer;

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

void cdecl tfunc(void);

void tfunc(void)
{
    TimerCount++;
    if( NET_Player_Count > 0 ) NET_Player_Count--;
    if( AuxInput == CONTROL_JOYSTICK ) JOYRead( Joy );
}

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

void TimerSetup()
{
    TimerCount=0;

    GameTimer = AIL_register_timer(&tfunc);

    AIL_set_timer_frequency(GameTimer,60);

    AIL_start_timer(GameTimer);
}

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

void TimerKill()
{
    AIL_release_timer_handle(GameTimer);
}

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

void WaitTimer()
{
    UDWORD t=TimerCount;

    while(TimerCount == t);
}

//===========================================================================
// Game play timing functions
//===========================================================================

clock_t CurrentTime, PauseTime;
UDWORD GameTick, GameClock;

inline UDWORD HZClock()
{
    return( TimerCount );
}

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

void StartGameClock()
{
    GameClock = HZClock();
}

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

void SetPauseTime()
{
    PauseTime = HZClock();
}

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

void ModifyGameClock()
{
    GameClock -= HZClock() - PauseTime;
}

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

UDWORD GetGameClock()
{
    CurrentTime = HZClock();
    return( ( CurrentTime + GameClock) / 60 );  // 60 Frames Per Second
}

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

void FormatTime( UDWORD time, BYTE *t )
{
    UDWORD hour,min,sec;
    BYTE h[8], m[8], s[8];

    hour = time / ( 60 * 60 );
    min  = ( time - (hour * (60*60))) / 60;
    sec  = ( time - (min * 60) - (hour * (60*60)));

    itoa( hour, h, 10 );
    itoa( min, m, 10 );
    itoa( sec, s, 10 );

    t[0] = h[0];  //Hour

    t[1] = ':';

    if( strlen( m ) == 1) {  //Min
        t[2] = '0';
        t[3] = m[0];
        }
    else {
        t[2] = m[0];
        t[3] = m[1];
        }

    t[4] = ':';

    if( strlen( s ) == 1) {  //Sec
        t[5] = '0';
        t[6] = s[0];
        }
    else {
        t[5] = s[0];
        t[6] = s[1];
        }
    t[7] = 0;
}

//===========================================================================
// Debug macros
//===========================================================================

#define DEBUG_INIT              { FILE *debugfile = fopen( "debug.txt", "a+t" ); }

#define DEBUG( a,b )                { fprintf( debugfile, a, b ); }

#define DEBUG_CLOSE( a,b )      { fclose( debugfile ); }

//===========================================================================
// Palette functions
//===========================================================================

void SPal(BYTE i,BYTE r,BYTE g,BYTE b)
{
    PalDac(i, (b << 16) + (g << 8) + r);
    GPalette[i*3]=r;
    GPalette[(i*3)+1]=g;
    GPalette[(i*3)+2]=b;
}

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

void BlackPal()
{
    for(UWORD i=0; i<256; i++) {
        PalDac(i, 0);
        }
    for(i=0; i<256; i++) {
        GPalette[i*3]=0;
        GPalette[(i*3)+1]=0;
        GPalette[(i*3)+2]=0;
        }
}

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

void SetPal(UBYTE *palette)
{
    UWORD pd=0;
    UBYTE *p,*g;

    g=GPalette;
    p=palette;
    pd=0;
    for(UWORD i=0;i<768;i+=3) {
        while((inp(0x03da) & 1)!=1); //wait for horizontal blank
        PalDac(pd, (*(p+2) << 16) + (*(p+1) << 8) + *p);
        pd++;
        p+=3; g+=3;
        }
    for(i=0;i<768;i+=3) {
        GPalette[i]=palette[i];
        GPalette[i+1]=palette[i+1];
        GPalette[i+2]=palette[i+2];
        }
}

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

void SetForFadeOut(UWORD fadefrom,UWORD fadeto)
{
    for(UWORD i=fadefrom*3;i<fadeto*3;i+=3) {
        APalette[i]=0;
        APalette[i+1]=0;
        APalette[i+2]=0;
        }
}

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

void CopyPal(UBYTE *palf,UBYTE *palt,UWORD f,UWORD t,UWORD s)
{
    UWORD pf=f*3,pt=t*3;

    for(UWORD i=0;i<s;i++) {
        palt[pt]=palf[pf];
        palt[pt+1]=palf[pf+1];
        palt[pt+2]=palf[pf+2];
        pt+=3; pf+=3;
        }
}

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

void FillPal( UBYTE *fp, UBYTE *tp, UWORD f, UWORD t, UWORD s )
{
    UWORD i;

    f *= 3;
    t *= 3;
    s *= 3;

    for( i=f; i<t; i+=3 ) {
        tp[i] = fp[s];
        tp[i+1] = fp[s+1];
        tp[i+2] = fp[s+2];
        }
}

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

void SetPalSeg(UBYTE *pal,UWORD f,UWORD t)
{
    UWORD pf, pt=t*3;

    for(pf=f*3; pf<pt; pf+=3) {
        while((inp(0x03da) & 1)!=1); //wait for horizontal blank
        PalDac(pf/3, (pal[pf+2] << 16) + (pal[pf+1] << 8) + pal[pf]);
        GPalette[pf] = pal[pf];
        GPalette[pf+1] = pal[pf+1];
        GPalette[pf+2] = pal[pf+2];
        }
}

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

BOOL MorphPalStep(UBYTE st,UBYTE en,UWORD speed,UBYTE *palette)
{
    UWORD p=0;
    WORD pval,pcmp;
    BOOL done=FALSE,change=FALSE;

    done=TRUE;
    for(UWORD i=0;i<768;i+=3) {
        if((i>=st*3) && (i<=en*3)) {
            change=FALSE;
            for(UWORD sp=i;sp<i+3;sp++) {
                if(GPalette[sp] != palette[sp]) {
                    pval=GPalette[sp];
                    pcmp=palette[sp];
                    if(pval < pcmp) {
                        pval+=speed;
                        if(pval > pcmp) pval=pcmp;
                        }
                    else {
                        pval-=speed;
                        if(pval < pcmp) pval=pcmp;
                        }
                    GPalette[sp]=pval;
                    done=FALSE;
                    change=TRUE;
                    }
                }
            if(change) {
                while((inp(0x03da) & 1)!=1); //wait for horizontal blank
                PalDac(p, (GPalette[i+2] << 16) + (GPalette[i+1] << 8) + GPalette[i]);
                }
            }
        p++;
        }
    return(done);
}

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

void MorphPal(UBYTE st,UBYTE en,UWORD speed,UBYTE *palette)
{
    while(!MorphPalStep(st,en,speed,palette)) {
        WaitTimer();
        }
}

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

// Special morph palette that updates graphics while morphing - temporary.

void GMorphPal(UBYTE st,UBYTE en,UWORD speed,UBYTE *palette)
{
    UWORD p=0;
    BOOL done=FALSE;

    void GRAPHICUpdate();

    while(!done) {
        done=TRUE;
        GRAPHICUpdate();
        for(UWORD i=0;i<768;i+=3) {
        if((i>=st*3) && (i<=en*3)) {
                for(UWORD sp=0;sp<speed;sp++) {
                    if(GPalette[i]!=palette[i]) {
                        if(GPalette[i]<palette[i]) GPalette[i]++;
                        else GPalette[i]--;
                        done=FALSE;
                        }
                    if(GPalette[i+1]!=palette[i+1]) {
                        if(GPalette[i+1]<palette[i+1]) GPalette[i+1]++;
                        else GPalette[i+1]--;
                        done=FALSE;
                        }
                    if(GPalette[i+2]!=palette[i+2]) {
                        if(GPalette[i+2]<palette[i+2]) GPalette[i+2]++;
                        else GPalette[i+2]--;
                        done=FALSE;
                        }
                    }
            }

            PalDac(p, (GPalette[i+2] << 16) + (GPalette[i+1] << 8) + GPalette[i]);
        p++;
        }
        }
}

//===========================================================================
// Map setup & offset tables setup
//===========================================================================

void Copy2Svga(UBYTE *buf)
{
    UWORD i;

    WaitTimer();
    for(i=0;i<5;i++) {
        Svga.SetBank(i);
        memcpy((BYTE *) 0xa0000,(BYTE *) buf+(i*65536),65536);
        }
}

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

void Copy2SvgaArea(UBYTE *buf)
{
    UWORD i;

    WaitTimer();
    for(i=0;i<4;i++) {
        Svga.SetBank(i);
        memcpy((BYTE *) 0xa0000,(BYTE *) buf+(i*65536),65536);
        }
    //Svga.SetBank(5);
    //memcpy((BYTE *) 0xa0000,(BYTE *) buf+(5*65536),20480);
    Svga.SetBank(4);
    memcpy((BYTE *) 0xa0000,(BYTE *) buf+(4*65536),24576);
}

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

void MAPUpdateSetup()
{
   TUPDATE *m;
   DWORD TopTile,BotTile;
   WORD TopBank,BotBank;
   DWORD x,y;
   WORD tile;

   for(UWORD k=0;k<5;k++) bankstart[k]=0;
   for(k=0;k<480;k++) Yoffset[k]=k*640;
   for(k=0;k<MAPHEIGHT;k++) Moffset[k]=k*40;

   tile=0; x=0; y=0;
   for(UWORD i=0;i<MAPHEIGHT;i++) {
      x=0;
      for(UWORD j=0;j<MAPWIDTH;j++) {
         m=&MAPUpdate[tile];
         m->update1=TRUE;
         m->update2=TRUE;
         m->x=x; m->y=y;

         TopTile=x+(y*640);
         BotTile=(x+16)+((y+16)*640);

         m->offset=TopTile;

         m->bank1=(TopTile & 0xffff0000)>>16;
         m->bank2=(BotTile & 0xffff0000)>>16;

         if(bankstart[m->bank1]==0) bankstart[m->bank1]=tile;
         if(bankstart[m->bank2]==0) bankstart[m->bank2]=tile;

         m->lines2=((((y+16)*640)+x)&0x0000ffff)/640;
         m->lines1=16-m->lines2;

         m->start1=(UBYTE *) 0xa0000+(((y*640)+x)&0x0000ffff);
         m->start2=(UBYTE *) 0xa0000+((((y+m->lines1)*640)+x)&0x0000ffff);
         m->phase=0;
         m->buftop1=(UBYTE *) buf1+((y*640)+x);
         m->bufbot1=(UBYTE *) buf1+(((y+m->lines1)*640)+x);
         m->buftop2=(UBYTE *) buf2+((y*640)+x);
         m->bufbot2=(UBYTE *) buf2+(((y+m->lines1)*640)+x);
         tile++;
         x+=16;
         }
     y+=16;
     }
   bankstart[0]=0;
}

//===========================================================================
// Map edge reset - resets edge of map for clipping
//===========================================================================

void MAPResetEdge()
{
    DWORD i;
   TUPDATE *m1,*m2;

   m1=&MAPUpdate[0];
    for(i=0;i<MAPWIDTH;i++) {
      m1->update1=FALSE;
      m1->update2=FALSE;
      m1->phase=0;

        (m1+MAPWIDTH)->update1=FALSE;
      (m1+MAPWIDTH)->update2=FALSE;
      (m1+MAPWIDTH)->phase=0;
        m1++;
        }

    m1=&MAPUpdate[0];
   m2=&MAPUpdate[MAPWIDTH-1];
    for(i=0;i<MAPHEIGHT;i++) {
      m1->update1=FALSE;
      m1->update2=FALSE;
      m1->phase=0;
      (m1+1)->update1=FALSE;
      (m1+1)->update2=FALSE;
      (m1+1)->phase=0;

        m2->update1=FALSE;
      m2->update2=FALSE;
      m2->phase=0;
        (m2-1)->update1=FALSE;
      (m2-1)->update2=FALSE;
      (m2-1)->phase=0;

        m1+=MAPWIDTH;
        m2+=MAPWIDTH;
        }
}

//===========================================================================
// Sprite Functions
//===========================================================================

void SPRITEResetAll()
{
    SPRITE *s=&GSprite[0];
    for(UWORD i=0;i<NUMSPRITES;i++) {
        s->active=FALSE;
        s->update=FALSE;
        s->forcedupdate=FALSE;
        s->pri=0;
        s->clag=0;
        s->anim=0;
        s->animpos=0;
        s->walk=FALSE;
        s->fly=FALSE;
        s->xmov=0;
        s->ymov=0;
        s->xcnt=0;
        s->ycnt=0;
        s->dline=NOLINE;
        s->spawnid=-1;
        s++;
        }
}

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

// Find free sprite

UWORD SPRITEGetFree()
{
   SPRITE *s=&GSprite[1];
   for(UWORD i=1;i<NUMSPRITES;i++) if((s++)->active==FALSE) return(i);
   return(0);
}

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

// Set sprite animation (Will not set if sprite already using same animation)

void SPRITEAnim(UWORD s,UWORD a,UWORD p)
{
    SPRITE *sa=&GSprite[s];

    if(sa->anim==a) return;
   sa->animpos=0;
   sa->anim=a;
   sa->image=NULL;
   sa->animtime=GOBJ[GSprite[s].id].ASpeed[a];
   sa->animcnt=1;
   sa->animtog=TRUE;
   sa->pri=p;
}

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

// Set sprite animation (Forced animation)

void SPRITENewAnim(UWORD s,UWORD a,UWORD p)
{
    SPRITE *sa=&GSprite[s];

    sa->animpos=0;
   sa->anim=a;
   sa->image=NULL;
   sa->animtime=GOBJ[GSprite[s].id].ASpeed[a];
   sa->animcnt=1;
   sa->animtog=TRUE;
   sa->pri=p;
}

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

// Work out line projection for flying sprite

void WorkoutLine(SPRITE *s,UWORD x1,UWORD y1,UWORD x2,UWORD y2)
{
   DWORD xDiff,yDiff,xInc,yInc,x,y;

   xDiff=((DWORD) abs(x1-x2)) << 16;
   yDiff=((DWORD) abs(y1-y2)) << 16;
   if (xDiff>yDiff) {
      xInc=1L<<16; yInc=yDiff/(xDiff>>16);
      }
   else {
      xInc=xDiff/(yDiff>>16); yInc=1L<<16;
      }
   if (x2 < x1) xInc= -xInc;
   if (y2 < y1) yInc= -yInc;
   x=((DWORD) x1) << 16;  y=((DWORD) y1) << 16;
   x+=xInc; y+=yInc;

   s->xinc=xInc;
   s->yinc=yInc;
   s->lpx=x;
   s->lpy=y;
   s->lx=x2;
   s->ly=y2;
}

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

// Move sprite along line (Increasing with velocity s->sline)

void SpriteLine(SPRITE *s)
{
   UWORD spd=s->sline;
    UWORD Dr;

    if(s->pline!=0) {
      s->pline--;
      return;
      }
    if(s->yinc<0) Dr=0;  //Dr0=Up&Right Dr1=Up&Left Dr2=Dn&Right Dr3=Dn&Left
    else Dr=2;
    if(s->xinc<0) Dr++;

    s->lpx+=(s->xinc*spd); s->lpy+=(s->yinc*spd);
   s->x=(s->lpx+32767L)>>16; s->y=(s->lpy+32767L)>>16;

    switch(Dr) {
        case 0: //Up&Right
            if(s->y<s->ly) s->y=s->ly;
            if(s->x>s->lx) s->x=s->lx;
            break;
        case 1: //Up&Left
            if(s->y<s->ly) s->y=s->ly;
            if(s->x<s->lx) s->x=s->lx;
            break;
        case 2: //Dn&Right
            if(s->y>s->ly) s->y=s->ly;
            if(s->x>s->lx) s->x=s->lx;
            break;
        case 3: //Dn&Left
            if(s->y>s->ly) s->y=s->ly;
            if(s->x<s->lx) s->x=s->lx;
            break;
        }

   if(s->sline<24) s->sline++;
}

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

// Prepare the sprites and map details for printing sprites

void SPRITEPrepare()
{
   TUPDATE *m;
   SPRITE *s,*n;
   UWORD sx,sy;
   UDWORD st;
   UWORD com,anm,spawnspr;
   BOOL  AnimUpdate;

   //Check spawning sprites

   s=&GSprite[0];
   for(UWORD i=0;i<NUMSPRITES;i++) {
     if(s->active==TRUE && s->spawnid!=-1) {
        if(s->spawncnt==0) {
           s->spawncnt=s->spawntime;
           spawnspr=SPRITEGetFree();
              n=&GSprite[spawnspr];
              n->id=s->spawnid;
              n->line=-1;
              n->walk=FALSE;
              n->fly=FALSE;
              n->oldx=0;
              n->oldy=0;
              n->xmov=0;
              n->ymov=0;
              n->x=s->x;
              n->y=s->y;
              n->active=TRUE;
              SPRITENewAnim(spawnspr,0,1);
           }
        else s->spawncnt--;
        }
        s++;
        }

   //1st Pass - update sprites that have moved/animated

   s=&GSprite[0];
   for(i=0;i<NUMSPRITES;i++) {
      if(s->active==TRUE) {
         AnimUpdate=FALSE;
         if(s->animtog==TRUE) {
                s->animcnt--;
                if(s->animcnt==0) {
                    s->animcnt=s->animtime;
                    GFRM=GOBJ[s->id].FRMList;
                    GDAT=GOBJ[s->id].GData;
                    GANM=GOBJ[s->id].AData;

                    com=GANM[s->anim][s->animpos].p1;
                    anm=GANM[s->anim][s->animpos].p2;

                if(com!=5) {
                        s->image=GDAT[GFRM[anm-1]];
                        AnimUpdate=TRUE;
                        s->animpos++;
                        com=GANM[s->anim][s->animpos].p1;
                        if(com==1 || com==2) s->animpos=0;
                        if(com==2) s->animtog=FALSE;
                        }
                    else {
                        s->active=FALSE;
                        s->clag=0;
                        s->xcnt=0; s->ycnt=0;
                        s->oldx=0; s->oldy=0;
                        s->spawnid=-1;
                        s->id=0;
                        s->dline=NOLINE;
                        AnimUpdate=TRUE;
                        }
                    }
            }
         if(s->clag>0) s->clag--;
         s->oldx=s->x; s->oldy=s->y;

         if(s->dline!=NOLINE) SpriteLine(s);
         if(s->xcnt!=0) {
               s->xcnt--;
               s->x+=s->xmov;
               }
         if(s->ycnt!=0) {
            s->ycnt--;
            s->y+=s->ymov;
            }

        if((s->x!=s->oldx)||(s->y!=s->oldy)||(AnimUpdate==TRUE)||(s->forcedupdate==TRUE)) {
                s->update=TRUE;
                s->forcedupdate=FALSE;
                sx=s->x; sy=s->y;
                st=Moffset[sy>>4]+(sx>>4);

                m=&MAPUpdate[st];
                m->phase=2;
                (m+1)->phase=2;
                (m+MAPWIDTH)->phase=2;
                (m+MAPWIDTH+1)->phase=2;
                if((sx & 0x000f) !=0) {
                    (m+2)->phase=2;
                    (m+MAPWIDTH+2)->phase=2;
                    }

                if((sy & 0x000f) !=0) {
                    (m+MAPWIDTH+MAPWIDTH)->phase=2;
                    (m+MAPWIDTH+MAPWIDTH+1)->phase=2;
                    if((sx & 0x000f) !=0) {
                        (m+MAPWIDTH+MAPWIDTH+2)->phase=2;
                        }
                    }
                }
        if(((s->x!=s->oldx)||(s->y!=s->oldy)) && s->oldx!=0 && s->oldy!=0) {
                sx=s->oldx; sy=s->oldy;
                st=Moffset[sy>>4]+(sx>>4);

                m=&MAPUpdate[st];
                if(m->phase==0) m->phase=1;
                if((m+1)->phase==0) (m+1)->phase=1;
                if((m+MAPWIDTH)->phase==0) (m+MAPWIDTH)->phase=1;
                if((m+MAPWIDTH+1)->phase==0) (m+MAPWIDTH+1)->phase=1;

                if((sx & 0x000f) !=0) {
                    if((m+2)->phase==0) (m+2)->phase=1;
                    if((m+MAPWIDTH+2)->phase==0) (m+MAPWIDTH+2)->phase=1;
                    }
                if((sy & 0x000f) !=0) {
                    if((m+MAPWIDTH+MAPWIDTH)->phase==0) (m+MAPWIDTH+MAPWIDTH)->phase=1;
                    if((m+MAPWIDTH+MAPWIDTH+1)->phase==0) (m+MAPWIDTH+MAPWIDTH+1)->phase=1;
                    if((sx & 0x000f) !=0) {
                        if((m+MAPWIDTH+MAPWIDTH+2)->phase==0) (m+MAPWIDTH+MAPWIDTH+2)->phase=1;
                        }
                    }
                }
        if((s->dline!=NOLINE) && (s->y==s->ly && s->x==s->lx)) {
                if(s->dline==SPRLINEKILL) {
                    s->active=FALSE;
                    s->clag=0;
                    }
                s->dline=NOLINE;
                s->sline=0;
                s->spawnid=-1;
                }
        }
      s++;
      }

   //2nd Pass - update sprites that have been overwritten by the 1st pass

   s=&GSprite[0];
   for(i=0;i<NUMSPRITES;i++) {
      if(s->active==TRUE && s->update==FALSE) {
            AnimUpdate=FALSE;
            sx=s->x; sy=s->y;
            st=Moffset[sy>>4]+(sx>>4);
            m=&MAPUpdate[st];
            if(m->phase>0) AnimUpdate=TRUE;
            if((m+1)->phase>0) AnimUpdate=TRUE;
            if((m+MAPWIDTH)->phase>0) AnimUpdate=TRUE;
            if((m+MAPWIDTH+1)->phase>0) AnimUpdate=TRUE;

            if((sx & 0x000f) !=0) {
                if((m+2)->phase>0) AnimUpdate=TRUE;
                if((m+MAPWIDTH+2)->phase>0) AnimUpdate=TRUE;
                }
            if((sy & 0x000f) !=0) {
                if((m+MAPWIDTH+MAPWIDTH)->phase>0) AnimUpdate=TRUE;
                if((m+MAPWIDTH+MAPWIDTH+1)->phase>0) AnimUpdate=TRUE;
                if((sx & 0x000f) !=0) {
                    if((m+MAPWIDTH+MAPWIDTH+2)->phase>0) AnimUpdate=TRUE;
                    }
                }

            if(AnimUpdate==TRUE) {
                s->update=TRUE;
                m->update1=TRUE;
                m->update2=TRUE;
                if((sx & 0x000f) !=0) {
                    (m+1)->update1=TRUE;
                    (m+1)->update2=TRUE;
                    (m+2)->update1=TRUE;
                    (m+2)->update2=TRUE;
                    }
                if((sy & 0x000f) !=0) {
                    (m+MAPWIDTH)->update1=TRUE;
                    (m+MAPWIDTH)->update2=TRUE;
                    (m+MAPWIDTH+MAPWIDTH)->update1=TRUE;
                    (m+MAPWIDTH+MAPWIDTH)->update2=TRUE;
                    if((sx & 0x000f) !=0) {
                        (m+MAPWIDTH+1)->update1=TRUE;
                        (m+MAPWIDTH+1)->update2=TRUE;
                        (m+MAPWIDTH+2)->update1=TRUE;
                        (m+MAPWIDTH+2)->update2=TRUE;
                        (m+MAPWIDTH+MAPWIDTH+1)->update1=TRUE;
                        (m+MAPWIDTH+MAPWIDTH+1)->update2=TRUE;
                        (m+MAPWIDTH+MAPWIDTH+2)->update1=TRUE;
                        (m+MAPWIDTH+MAPWIDTH+2)->update2=TRUE;
                        }
                    }
                }
        }
    s++;
    }
}

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

// Refresh sprite buffer with 16x16 from background buffer

void BACKRefresh()
{
   TUPDATE *m;

   m=&MAPUpdate[0];
   for(UWORD j=0;j<MAPSIZE;j++) {
        if(m->phase!=0) {
        m->update1=TRUE;
        m->update2=TRUE;
        BUFBlitTile32x32(m->buftop2,m->buftop1,16);
        m->phase--;
        }
        m++;
        }
}

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

// Refresh sprite buffer update values

void BACKMapRefresh()
{
   TUPDATE *m;

   m=&MAPUpdate[0];
   for(UWORD j=0;j<MAPSIZE;j++) {
        if(m->phase!=0) {
        m->update1=TRUE;
        m->update2=TRUE;
        m->phase--;
        }
        m++;
        }
}

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

// Print active sprite images to sprite buffer

void SPRITEUpdate()
{
   TUPDATE *m;
   SPRITE *s;
   UWORD sx,sy;
   UDWORD st;

    s=&GSprite[0];
    for(UWORD i=0;i<NUMSPRITES;i++) {
        if(s->active==TRUE && s->update==TRUE && s->pri==2) {
        sx=s->x; sy=s->y;
            BlitComp8Sprite32x32(s->image,buf1+Yoffset[sy]+sx);
        s->update=FALSE;
        }
        s++;
        }
    s=&GSprite[0];
    for(i=0;i<NUMSPRITES;i++) {
        if(s->active==TRUE && s->update==TRUE && s->pri==1) {
        sx=s->x; sy=s->y;
            BlitComp8Sprite32x32(s->image,buf1+Yoffset[sy]+sx);
        s->update=FALSE;
        }
        s++;
        }
    s=&GSprite[0];
    for(i=0;i<NUMSPRITES;i++) {
        if(s->active==TRUE && s->update==TRUE && s->pri==0) {
        sx=s->x; sy=s->y;
            BlitComp8Sprite32x32(s->image,buf1+Yoffset[sy]+sx);
        s->update=FALSE;
        }
        s++;
        }
}

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

// Update sprites to buffer 2

void SPRITEUpdateBuffer2()
{
   TUPDATE *m;
   SPRITE *s;
   UWORD sx,sy;
   UDWORD st;

    s=&GSprite[0];
    for(UWORD i=0;i<NUMSPRITES;i++) {
        if(s->active==TRUE && s->update==TRUE && s->pri==2) {
        sx=s->x; sy=s->y;
            BlitComp8Sprite32x32(s->image,buf2+Yoffset[sy]+sx);
        s->update=FALSE;
        }
        s++;
        }
    s=&GSprite[0];
    for(i=0;i<NUMSPRITES;i++) {
        if(s->active==TRUE && s->update==TRUE && s->pri==1) {
        sx=s->x; sy=s->y;
            BlitComp8Sprite32x32(s->image,buf2+Yoffset[sy]+sx);
        s->update=FALSE;
        }
        s++;
        }
    s=&GSprite[0];
    for(i=0;i<NUMSPRITES;i++) {
        if(s->active==TRUE && s->update==TRUE && s->pri==0) {
        sx=s->x; sy=s->y;
            BlitComp8Sprite32x32(s->image,buf2+Yoffset[sy]+sx);
        s->update=FALSE;
        }
        s++;
        }
}

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

// Imprint font sprites onto all buffers

void FONTImprint()
{
    TUPDATE *m;
    SPRITE *s;
    UWORD sx,sy;
    UDWORD st;

    s=&GSprite[0];
    for(UWORD i=0;i<NUMSPRITES;i++) {
        if(s->active==TRUE && s->update==TRUE) {
        sx=s->x; sy=s->y;
            BlitComp8SSprite32x32(s->image,buf1+Yoffset[sy]+sx);
            BlitComp8SSprite32x32(s->image,buf2+Yoffset[sy]+sx);
        s->update=FALSE;
        }
        s++;
        }
}

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

// Update changed 32x32's from the logical buffer to the physical screen

void MAPRefresh()
{
    TUPDATE *m;

    for(UWORD b=0;b<5;b++) {
        Svga.SetBank(b);
        UWORD i=bankstart[b];
        m=&MAPUpdate[i];
        do {
        if(m->update1==TRUE) {
                if(m->bank1==b) {
                if(m->bank1==m->bank2) BUFBlitTile32x32(m->buftop1,m->start1,16);
                else BUFBlitTile32x32(m->buftop1,m->start1,m->lines1);
                    m->update1=FALSE;
                }
                }
        else {
                if((m->bank1!=m->bank2) && m->bank2==b) {
                if(m->update2==TRUE) BUFBlitTile32x32(m->bufbot1,m->start2,m->lines2);
                m->update2=FALSE;
                    }
                }
        m++;
        } while(i++<MAPSIZE && (m->bank1==b || m->bank2==b));
        }
}

//===========================================================================
// Game graphic refresh functions
//===========================================================================

// Network update function

void NET_Update()
{
    if( NET_Installed && ( GameMode == MULTI ) ) {
        NET_Player_Score = GScore;
        NETUserCycle();
        }
}

// In-Game graphic update

void GRAPHICUpdate()
{
    WaitTimer();

    NET_Update();

    MAPRefresh();
    SPRITEPrepare();
    BACKRefresh();
    SPRITEUpdate();
}

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

void GRAPHICUpdateNoVr()
{
    MAPRefresh();
    SPRITEPrepare();
    BACKRefresh();
    SPRITEUpdate();
}

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

void GRAPHICUpdateNotVisible()
{
    SPRITEPrepare();
    SPRITEUpdate();
}

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

void PANELUpdate()
{
    extern void ScoreTrack();
   extern void KillsTrack();
   extern void LevelsTrack();

    ScoreTrack();
   KillsTrack();
   LevelsTrack();
}

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

// Fixed font update (same as sprite but leaves imprint of image)

void FONTUpdate()
{
    MAPRefresh();

    SPRITEPrepare();
    BACKRefresh();
    FONTImprint();
}

//===========================================================================
// Main Game functions
//===========================================================================

void GLineSetup()
{
    DWORD i;

    for(i=0;i<NUMLINES;i++) {
        GLine[i].backcolor = 0;
        GLine[i].count = 0;
        GLine[i].wait = 0;
        }
}

// Game setup

void GSetup()
{
    TUPDATE *m;

    m=&MAPUpdate[0];
    for(UWORD j=0;j<MAPSIZE;j++) {
        m->update1=TRUE;
        m->update2=TRUE;
        m++;
        }
    memcpy(buf1,buf2,300000);

    for(UWORD i=0;i<NUMSPRITES;i++) {
        GSprite[i].active=FALSE;
        GSprite[i].forcedupdate=FALSE;
        GSprite[i].clag=0;
        GSprite[i].update=1;
        GSprite[i].xmov=0;
        GSprite[i].ymov=0;
        GSprite[i].xcnt=0;
        GSprite[i].ycnt=0;
        GSprite[i].dline=NOLINE;
        GSprite[i].walk=FALSE;
        GSprite[i].fly=FALSE;
        GSprite[i].spawnid=-1;
        GSprite[i].lx=0;
        GSprite[i].ly=0;
        }

    GLastQuad=-1;
    GLineSetup();

    for(i=0;i<768;i++) {
        WPalette[i]=63;
        TPalette[i]=63;
        }
    TPalette[0]=0; TPalette[1]=0; TPalette[2]=0;

    // Real state variables

    OldLevel=-1;
    GScore=0; GBScore=0; Accum=1; GBKills=0; TotalKills=0;
    GPlayerFacing=FACEUP;
    LastKill=-1;
    LastLineKill=0;
    WalkLine=-1;
    FireWait=0;
    NEWspr=-1;
    MOVquadline=-1;
    AppearCount=-1;
    LastBombQuad=-1;
    NumBombs=0;
    Strobe=FALSE;
    StrobeVal1=0;
    StrobeVal2=0;
    StrobeSound=0;
    Bomb2Sound=FALSE;
    SpringFull=0;
    BonusCount=0;
    DoingLeadIn=FALSE;
    FireEnableStatus=TRUE;
    LineState=BEING_BUILT;

    // Events

    EVENT_AddBlock = 0;
    EVENT_Bonus = 0;
    EVENT_SpeedUp = 0;
    SpeedUpDuration = 0;
    EVENT_BlowUp = 0;
    EVENT_AddTarget = 0;
    EVENT_Pause = FALSE;

    NETSEQUENCE = 0;
    NETSEQUENCECOUNT = 0;

    // Timer

    TimerCount = 0;
}

//---------------------------------------------------------------------------
// Set network player name in sprites
//---------------------------------------------------------------------------

void NetSetName( DWORD x, BYTE *s )
{
    DWORD i;
    UWORD fs;
    BYTE *p = s, c;

    for( i=0; i<5; i++ ) {
        c = *p;
        if( c == 0 ) c = 32;
        else p++;

        if( c != NET_Name_Letter[ x + i ] ) {
            NET_Name_Letter[ x + i ] = c;
            fs = NET_Name_Sprite[ x + i ];
            GSprite[fs].active=TRUE;
            GSprite[fs].id=NETFONT;
            if( c  >='A' &&  c  <='Z') {
            SPRITENewAnim(fs,( c - 65),0);
            }
            if( c ==' ') {
            SPRITENewAnim(fs,47,0);
            }
            if( c =='!') {
            SPRITENewAnim(fs,36,0);
            }
            if( c =='+') {
            SPRITENewAnim(fs,37,0);
            }
            if( c =='-') {
            SPRITENewAnim(fs,38,0);
            }
            if( c =='.') {
            SPRITENewAnim(fs,39,0);
            }
            if( c =='?') {
            SPRITENewAnim(fs,40,0);
            }
            if( c =='{') {
            SPRITENewAnim(fs,41,0);
            }
            if( c =='}') {
            SPRITENewAnim(fs,42,0);
            }
            if( c =='[') {
            SPRITENewAnim(fs,43,0);
            }
            if( c ==']') {
            SPRITENewAnim(fs,44,0);
            }
            if( c =='#') {
            SPRITENewAnim(fs,45,0);
            }
            if( c ==':') {
            SPRITENewAnim(fs,46,0);
            }
            if( c >='0' &&  c <='9') {
            SPRITENewAnim(fs,( c - 48 ) + 26,0);
            }
            }
        }
}

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

void Net_Refresh_Names()
{
    BYTE *n;
    BYTE blank[] = "     ";
    DWORD p;

    if( NET_Game == 0 ) return;

    NetSetName( 0, NET_Player_Name );

    for( p=0; p<3; p++ ) {
        n = NETGetPlayerName( p );
        if( n != NULL ) {
            if( NETGetPlayerType( p ) == NET_GAME_PLAYER ) NetSetName( ( p+1 ) * 5, n );
            }
        else NetSetName( ( p+1 ) * 5, blank );
        }
}

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

BOOL Net_Check_Won()
{
    if( NET_Game == 0 ) return( FALSE );
    if( NETFindGamePlayer() ) return( FALSE );
    if( NET_Player_Count == 0 ) return( TRUE );
    return( FALSE );
}

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

void KillHarmless( WORD i, UWORD l )
{
    SPRITE *s = &GSprite[i];
    BOOL KillSp;

    if( !s->active ) return;

   KillSp=FALSE;
   if((s->id>COLOR4 && ( s->id<EXPLODE) || (s->id==BONUSB)) && s->line == l ) {
    if(s->quad==0 && s->y==160-32) KillSp=TRUE;
    if(s->quad==1 && s->x==352+32) KillSp=TRUE;
    if(s->quad==2 && s->y==256+32) KillSp=TRUE;
    if(s->quad==3 && s->x==256-32) KillSp=TRUE;
    }
   if(KillSp==TRUE && s->fly!=TRUE) {
    s->id=EXPLODE;
    s->walk=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    SPRITENewAnim(i,0,0);
    }
}

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

// Set all the sprites next to each other on a line to move and animate

void MoveLine(UWORD l,WORD mx,WORD my,WORD cx,WORD cy,WORD anm)
{
    WORD QMove[10];
    WORD QNum;
    WORD QLine=(l-1)>>2;
    WORD i,j,tmp,cp;
    BOOL StopIt;
    BOOL MoveIt;
    SPRITE *s;

    //Figure out which sprites are on a line and sort them to lowest first
    QNum=0;
    s=&GSprite[1];
    if(QLine==1 || QLine==3) {  //Use X to sort
        for(i=1;i<NUMSPRITES;i++) {
            KillHarmless( i, l );   //Kill bombs/springs on moving to middle
            if(s->active==TRUE && (s->id<EXPLODE || (s->id==BONUSB)) && s->line==l) {
                QMove[QNum]=i;
                if(QNum!=0) {  //Sort to correct place in queue
                    cp=s->x;
                    j=QNum-1;
                    while((j!=-1) && (cp<GSprite[QMove[j]].x)) {
                        QMove[j+1]=QMove[j];
                        QMove[j]=i;
                        j--;
                    }
                    }
                QNum++;
            }
            s++;
        }
        }
    else {  //Use Y to sort
        for(i=1;i<NUMSPRITES;i++) {
            KillHarmless( i, l );   //Kill bombs/springs on moving to middle
            if(s->active==TRUE && (s->id<EXPLODE || (s->id==BONUSB)) && s->line==l) {
                QMove[QNum]=i;
                if(QNum!=0) {  //Sort to correct place in queue
                    cp=s->y;
                    j=QNum-1;
                    while((j!=-1) && (cp<GSprite[QMove[j]].y)) {
                        QMove[j+1]=QMove[j];
                        QMove[j]=i;
                        j--;
                    }
                    }
                QNum++;
            }
            s++;
        }
        }

    if(QNum==0) return;  //No sprites at all on this line!

    StopIt=FALSE;
    switch(QLine) {
        case 0: //Up
            for(i=0;(i<QNum) && (StopIt==FALSE);i++) {
                MoveIt=FALSE;
                s=&GSprite[QMove[i]];
                if(i!=0) {
                    if(s->y == (GSprite[QMove[i-1]].y+32)) MoveIt=TRUE;  //Joined
                    else StopIt=TRUE; // A split line - continue no further!
                    }
                else {
                    if(s->y == 0) MoveIt=TRUE; //First on line
                    else StopIt=TRUE; //No link to start of line
                    }
                if(MoveIt==TRUE) {
                s->xmov=mx; s->ymov=my;
                s->xcnt=cx; s->ycnt=cy;
                if(s->id<BONUSB) SPRITENewAnim(QMove[i],anm,1);
                else SPRITENewAnim(QMove[i],(NET_Game * 5),1);
                s->walk=TRUE;
                WalkLine=l;
                    }

                }
            break;

        case 3: //Left
            for(i=0;(i<QNum) && (StopIt==FALSE);i++) {
                MoveIt=FALSE;
                s=&GSprite[QMove[i]];
                if(i!=0) {
                    if(s->x == (GSprite[QMove[i-1]].x+32)) MoveIt=TRUE;  //Joined
                    else StopIt=TRUE; // A split line - continue no further!
                    }
                else {
                    if(s->x == 0) MoveIt=TRUE; //First on line
                    else StopIt=TRUE; //No link to start of line
                    }
                if(MoveIt==TRUE) {
                s->xmov=mx; s->ymov=my;
                s->xcnt=cx; s->ycnt=cy;
                if(s->id<BONUSB) SPRITENewAnim(QMove[i],anm,1);
                else SPRITENewAnim(QMove[i],(NET_Game * 5),1);
                s->walk=TRUE;
                WalkLine=l;
                    }

                }
            break;
        case 1: //Right
            for(i=QNum-1;(i>-1) && (StopIt==FALSE);i--) {
                MoveIt=FALSE;
                s=&GSprite[QMove[i]];
                if(i!=QNum-1) {
                    if(s->x == (GSprite[QMove[i+1]].x-32)) MoveIt=TRUE;  //Joined
                    else StopIt=TRUE; // A split line - continue no further!
                    }
                else {
                    if(s->x == 608) MoveIt=TRUE; //First on line
                    else StopIt=TRUE; //No link to start of line
                    }
                if(MoveIt==TRUE) {
                s->xmov=mx; s->ymov=my;
                s->xcnt=cx; s->ycnt=cy;
                if(s->id<BONUSB) SPRITENewAnim(QMove[i],anm,1);
                else SPRITENewAnim(QMove[i],(NET_Game * 5),1);
                s->walk=TRUE;
                WalkLine=l;
                    }

                }
            break;
        case 2: //Bottom
            for(i=QNum-1;(i>-1) && (StopIt==FALSE);i--) {
                MoveIt=FALSE;
                s=&GSprite[QMove[i]];
                if(i!=QNum-1) {
                    if(s->y == (GSprite[QMove[i+1]].y-32)) MoveIt=TRUE;  //Joined
                    else StopIt=TRUE; // A split line - continue no further!
                    }
                else {
                    if(s->y == 416) MoveIt=TRUE; //First on line
                    else StopIt=TRUE; //No link to start of line
                    }
                if(MoveIt==TRUE) {
                s->xmov=mx; s->ymov=my;
                s->xcnt=cx; s->ycnt=cy;
                if(s->id<BONUSB) SPRITENewAnim(QMove[i],anm,1);
                else SPRITENewAnim(QMove[i],(NET_Game * 5),1);
                s->walk=TRUE;
                WalkLine=l;
                    }

                }
            break;
        }
}

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

// Workout the number of sprites in all lines and the number of Spring icons

void FindLineCount()
{
    SPRITE *s;
    s=&GSprite[0];

    for(UWORD i=0;i<NUMLINES;i++) {
        GLine[i].count=0;
        }
    BonusCount=0;

    NET_Player_Blocks = 0;
    NET_Player_Springs = SpringFull;

    for(i=1;i<NUMSPRITES;i++) {
        if(s->active==TRUE && s->line>0 && (s->id<EXPLODE || (s->id==BONUSB))) {
        GLine[s->line-1].count++;
        if(s->id==BONUSB) {
                BonusCount++;
                }
        NET_Player_Blocks++;
            }
        s++;
        }
    for(i=0;i<NUMLINES;i++) {
        if(GLine[i].count==0) GLine[i].wait = 0;
        else if(GLine[i].wait > 0) GLine[i].wait--;
        }
}

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

// Find the number of bombs in existence

void FindNumBombs()
{
    SPRITE *s;
    s=&GSprite[0];

    NumBombs=0;
    for(UWORD i=1;i<NUMSPRITES;i++) {
        if(s->active==TRUE && (s->id>=BOMB1 && s->id<=BOMB3)) NumBombs++;
        s++;
        }
}

//===========================================================================
// Create enemy functions
//
// Main logic for new sprite selection
// Only selects sprite attributes to appear, counter are set to delay
// the new sprites appearence and a 'strobe' sprite is created and placed
// where the new enemy will appear
//===========================================================================

inline DWORD RandQuad()
{
    DWORD q = rand() & 63;

    // Give side quadrants more chance because of ratio 8:5
    if( q<18 ) q = 1;
    else if( q<36 ) q = 3;
        else if( q<50 ) q = 0;
        else q = 2;

    return( q );
}

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

inline DWORD RandLine()
{
  return( rand() & 3 );
}

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

BOOL SelectLine( UWORD &q, UWORD &l )
{
    DWORD i;
    UWORD rquad,rline,minl,maxl,quadline,rsl,minw,qd,cposs;

    // Select random quad & line
    rquad = RandQuad();
    if( rquad == GLastQuad ) rquad = (rquad+1) & 3;
    GLastQuad = rquad;
    rline = RandLine();
    quadline = (rquad*4)+rline;

    //Allow chance to select line with lowest count for distribution spread
    cposs=rand() & 63;
    if(cposs < 20) {
        minl = 99;
        qd = rquad * 4;

        for( i=0; i<4; i++ ) {
            if( GLine[qd+i].count < minl ) {
                minl = GLine[qd+i].count;
                rline = i;
                }
            }
        quadline = qd + rline;
        }

    if( GLine[quadline].wait != 0 ) {
        //Look for wait 0
        rsl = rline;
        qd = rquad * 4;
        rline = ( rline + 1 ) & 3;
        while( rline != rsl ) {
            if( GLine[qd+rline].wait == 0 ) {
                rsl = 99; //Found
                break;
                }
            rline = ( rline + 1 ) & 3;
            }

        if( rsl != 99 ) { //Free line not found
            rsl = 99;
            minw = 999;
            for ( i=0; i < NUMLINES; i++ ) {
                if( GLine[i].wait > 0 && GLine[quadline].wait < minw ) {
                    minw = GLine[i].wait;
                    rsl = i;
                    }
                }
            if( rsl == 99 ) return( FALSE );
            rquad = rsl >> 2;
            rline = rsl & 3;
            }
        }

    q = rquad;
    l = rline;

    return( TRUE );
}

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

void CreateStrobe( WORD xpos, WORD ypos )
{
    SPRITE *ns;

    //Create strobe sprite
    NEWspr=SPRITEGetFree();
    ns=&GSprite[NEWspr];
    ns->id=STROBE;
    ns->line=-1;
    ns->walk=FALSE;
    ns->fly=FALSE;
    ns->oldx=0;
    ns->oldy=0;
    ns->xmov=0;
    ns->ymov=0;
    ns->x=xpos;
    ns->y=ypos;
    ns->active=TRUE;
    SPRITENewAnim(NEWspr,0,2);
}


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

void CreateEnemy()
{
    UWORD rquad,rline,rcolor,minl,quadline,cposs,i,maxl;
    WORD  xpos,ypos,mx,my,cx,cy,anm,aspeed,bcolor;

    if(NEWspr!=-1) return;

    if( DoingLeadIn ) {  // Initial difficulty creation
        LineState = BEING_BUILT;
        rquad = RandQuad();
        if(rquad==GLastQuad) rquad=(rquad+1) & 3;
        GLastQuad=rquad;
        rline = RandLine();
        quadline=(rquad*4)+rline+1;

        if(rquad==0 || rquad==2) maxl=3;  //top or bottom - 2
        else maxl=6; //left or right - 2
        while(GLine[quadline-1].count >= maxl) {
            rquad = RandQuad();
            rline = RandLine();
            quadline=(rquad*4)+rline+1;

            if(rquad==0 || rquad==2) maxl=3;  //top or bottom
            else maxl=6; //left or right
            }
        }
    else {
        if( SelectLine( rquad, rline ) == FALSE ) return;
      quadline=(rquad*4)+rline+1;
        }

    if(rquad==0 || rquad==2) maxl=5;  //top or bottom
    else maxl=8; //left or right

    rcolor=rand() & 3;     //change 3 here for more colors
    cposs=rand() & 63;
    // If GColorThreshold is higher there is more chance that color will be same
    if(cposs<GColorThreshold && GLine[quadline-1].count!=0 && GLine[quadline-1].backcolor<4)
        rcolor=GLine[quadline-1].backcolor;

    FindNumBombs();
    if(((rand() & 63) < 4) && (NumBombs<LevelBombs) && (LastBombQuad!=rquad)) {
        if(((rand() & 63) < 22) && ((WhichBombs & 1) !=0)) rcolor=BOMB1;
        else if(((rand() & 63) < 44) && ((WhichBombs & 2) !=0)) rcolor=BOMB2;
        else if((WhichBombs & 4) !=0) rcolor=BOMB3;
        LastBombQuad=rquad;
        }

//test

    if( ( rand() & 63 ) < 32 ) rcolor = BOMB1;

//test

    // Probability check for bonus letter (don't do if leading in)
    if(!(DoingLeadIn && (GameMode == CONTINUE))) {
        if(((rand() & 127)< BonusRand) && ((rand() & 127)< BonusRand)) {
            if(BonusCount < (5-SpringFull)) rcolor=BONUSB;
            }
        }

    switch(rquad) {
        case 0:
            xpos=256+(rline<<5);
            ypos=0;
            mx=0; my=2;
            cx=0; cy=16;
            anm=AWDOWN;
            break;
        case 1:
            xpos=608;
            ypos=160+(rline<<5);
            mx=-2; my=0;
            cx=16; cy=0;
            anm=AWLEFT;
            break;
        case 2:
            xpos=256+(rline<<5);
            ypos=416;
            mx=0; my=-2;
            cx=0; cy=16;
            anm=AWUP;
            break;
        case 3:
            xpos=0;
            ypos=160+(rline<<5);
            mx=2; my=0;
            cx=16; cy=0;
            anm=AWRIGHT;
            break;
        }

    GLine[quadline-1].wait=5;
    if(((rquad==0 || rquad==2) && GLine[quadline-1].count==4)
    || ((rquad==1 || rquad==3) && GLine[quadline-1].count==7)) {
            GLine[quadline-1].wait = 20;
            }

    //Set holdback to higher value if line is full to give player a fighting chance
    HoldBack=16; HoldLine=-1;
    if(((rquad==0 || rquad==2) && GLine[quadline-1].count==5)
    || ((rquad==1 || rquad==3) && GLine[quadline-1].count==8)) {
            HoldBack = 64;
            }

    //Set attributes for this sprite when it appears
    MOVquadline=quadline;
    MOVmx=mx; MOVmy=my;
    MOVcx=cx; MOVcy=cy;
    MOVanm=anm;

    if(DoingLeadIn==TRUE) MOVanm-=4;

    NEWxpos=xpos; NEWypos=ypos;
    NEWrquad=rquad; NEWrcolor=rcolor;
    NEWquadline=quadline;

    CreateStrobe( xpos, ypos );

    AppearCount=HoldBack+TimeSpeed;
}

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

// Monitor walking sprites and flag them as stopped walking if necessary

void MonitorWalking()
{
    SPRITE *s;
    UWORD espr,NWalk=0;

    s=&GSprite[1];
    for(UWORD i=1;i<NUMSPRITES;i++) {
        if(s->active==TRUE && s->walk==TRUE) {
        if(s->xcnt==0 && s->ycnt==0) {
                UWORD rquad=s->quad;
                if(rquad==1) rquad=3;
                else if(rquad==3) rquad=1;
                if(s->id<BONUSB) SPRITENewAnim(i,rquad,1);
                else SPRITENewAnim(i,(NET_Game * 5),1);
                s->walk=FALSE;
                WalkLine=-1;
                }
        }
        s++;
        }
}

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

void NewEnemy()
{
    if(MOVquadline!=-1 && AppearCount<16) {
        MoveLine(MOVquadline,MOVmx,MOVmy,MOVcx,MOVcy,MOVanm);
        MOVquadline=-1;
        }
}

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

void NewInstantEnemy()
{
    if(MOVquadline!=-1) {
        MoveLine(MOVquadline,MOVmx*MOVcx,MOVmy*MOVcy,1,1,MOVanm);
        MOVquadline=-1;
        }
}

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

// Initialise new enemy sprite based on CreateEnemy() attributes

void EnemyAppear()
{
    SPRITE *ne;
    UWORD espr;

    if(AppearCount==0) {
        AppearCount=-1;
        espr=SPRITEGetFree();
        ne=&GSprite[espr];
        ne->active=TRUE;
        ne->x=NEWxpos;
        ne->y=NEWypos;
        ne->quad=NEWrquad;
        ne->line=NEWquadline;
        ne->id=NEWrcolor;
        ne->clag=6;
        ne->walk=FALSE;
        ne->fly=FALSE;

        //if(NEWrcolor < 4) SoundPlay(NEWrcolor);

        if(NEWrquad==1) NEWrquad=3;
        else if(NEWrquad==3) NEWrquad=1;

        if(NEWrcolor<BONUSB) SPRITENewAnim(espr,NEWrquad,1);
        else SPRITENewAnim(espr,1 + (NET_Game * 5),1);
        GLine[NEWquadline-1].backcolor=NEWrcolor;

        if(DoingLeadIn==FALSE) {
            espr=SPRITEGetFree();
            ne=&GSprite[espr];
            ne->id=TWINKLE;
            ne->line=-1;
            ne->walk=FALSE;
            ne->fly=FALSE;
            ne->oldx=0;
            ne->oldy=0;
            ne->xmov=0;
            ne->ymov=0;
            ne->x=NEWxpos;
            ne->y=NEWypos;
            ne->active=TRUE;
            SPRITENewAnim(espr,0,0);
            }

        //Remove strobe
        ne=&GSprite[NEWspr];
        ne->id=0;
        ne->walk=FALSE;
        ne->fly=FALSE;
        ne->line=-1;
        ne->xmov=0;
        ne->ymov=0;
        ne->oldx=0;
        ne->oldy=0;
        ne->clag=0;
        ne->spawnid=-1;
        ne->active=FALSE;
        NEWspr=-1;
        LineState = BUILT;
        }
        else {
        if(AppearCount>0) AppearCount--;
        }
}

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

// Return the first sprite number on line 'l'

UWORD FirstOnLine(UWORD l)
{
    SPRITE *s,*e;
    s=&GSprite[0];
    e=&GSprite[1];
    WORD r=(l-1)>>2;
    WORD mnv,cl;
    UWORD i;

    if(GLine[l-1].count==0) return(0);
    cl=0;

    switch(r) {
        case 0:
        mnv=0;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->y>=mnv) {
                        mnv=e->y;
                        cl=i;
                        }
                    }
                    e++;
                }
        break;
        case 1:
        mnv=999;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->x<=mnv) {
                    mnv=e->x;
                        cl=i;
                        }
                    }
                    e++;
                }
        break;
        case 2:
        mnv=999;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->y<=mnv) {
                        mnv=e->y;
                        cl=i;
                        }
                    }
                    e++;
                }
        break;
        case 3:
        mnv=0;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->x>=mnv) {
                        mnv=e->x;
                        cl=i;
                        }
                    }
                e++;
                }
        break;
        }

    return(cl);
}

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

// Return the last sprite number on line 'l'

UWORD LastOnLine(UWORD l)
{
    SPRITE *s,*e;
    s=&GSprite[0];
    e=&GSprite[1];
    WORD r=(l-1)>>2;
    WORD mnv,cl;
    UWORD i;

    if(GLine[l-1].count==0) return(0);
    cl=0;

    switch(r) {
        case 0:
        mnv=999;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->y<=mnv) {
                        mnv=e->y;
                        cl=i;
                        }
                    }
                    e++;
                }
        break;
        case 1:
        mnv=0;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->x>=mnv) {
                    mnv=e->x;
                        cl=i;
                        }
                    }
                    e++;
                }
        break;
        case 2:
        mnv=0;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->y>=mnv) {
                        mnv=e->y;
                        cl=i;
                        }
                    }
                    e++;
                }
        break;
        case 3:
        mnv=999;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->x<=mnv) {
                        mnv=e->x;
                        cl=i;
                        }
                    }
                e++;
                }
        break;
        }
    return(cl);
}


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

// Return sprite number that player has collided with (or 0 if none)

UWORD CheckCollision(UWORD l,WORD xoff,WORD yoff)
{
    SPRITE *s,*e,*hit;
    s=&GSprite[0];
    e=&GSprite[1];
    WORD sx=s->x+xoff,sy=s->y+yoff;
    WORD sxx=sx+32,syy=sy+32;
    WORD r=(l-1)>>2;
    WORD mnv,cl;
    UWORD i;

    if(GLine[l-1].count==0) return(0);
    cl=0;

    switch(r) {
        case 0:
        mnv=0;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->y>=mnv) {
                        mnv=e->y;
                        hit=e;
                        cl=i;
                        }
                    }
                    e++;
                }
        break;
        case 1:
        mnv=999;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->x<=mnv) {
                    mnv=e->x;
                        hit=e;
                        cl=i;
                        }
                    }
                    e++;
                }
        break;
        case 2:
        mnv=999;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->y<=mnv) {
                        mnv=e->y;
                        hit=e;
                        cl=i;
                        }
                    }
                    e++;
                }
        break;
        case 3:
        mnv=0;
        for(i=1;i<NUMSPRITES;i++) {
                if(e->active==TRUE && (e->id<EXPLODE || (e->id==BONUSB)) && e->line==l) {
                    if(e->x>=mnv) {
                        mnv=e->x;
                        hit=e;
                        cl=i;
                        }
                    }
                e++;
                }
        break;
        }

    e=hit;
    if(!((sxx < e->x) || (sx > e->x+32) || (syy < e->y) || (sy > e->y+32))) return(cl);
    return(0);
}

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

// Check for enemy sprites walking into centre square (end of game)

BOOL GCheckGameOver()
{
    BOOL gover=FALSE,KillSp;
    DWORD maxl;
    SPRITE *s;

    Strobe=FALSE;

    s=&GSprite[1];
    for(UWORD i=1;i<NUMSPRITES;i++) {
        if( s->active ) {
            //Middle square breached
            if( s->id<EXPLODE ) {
            if(s->quad==0 && s->y==160) gover=TRUE;
            if(s->quad==1 && s->x==352) gover=TRUE;
            if(s->quad==2 && s->y==256) gover=TRUE;
            if(s->quad==3 && s->x==256) gover=TRUE;
            }
            //Check for killer touching middle square
        if(s->quad==0 || s->quad==2 ) maxl = 5;
            else maxl = 8;

            if( s->id<PCOLOR1 && GLine[s->line-1].count == maxl ) {
            if(s->quad==0 && s->y==160-32) Strobe=TRUE;
            if(s->quad==1 && s->x==352+32) Strobe=TRUE;
            if(s->quad==2 && s->y==256+32) Strobe=TRUE;
            if(s->quad==3 && s->x==256-32) Strobe=TRUE;
            }
            }
        s++;
        }

    if(Strobe==TRUE) {
        LineState = WILL_KILL;
        StrobeSound--;
        if(StrobeSound<0) {
            SoundPlay(5); // this needs to on another channel
            StrobeSound=40;
            }
        SPal(StrobeColor1,StrobeVal1+=4,0,0);
        GPalette[StrobeColor1*3]=StrobeVal1-4;
        if(StrobeColor1!=StrobeColor2) {
            SPal(StrobeColor2,StrobeVal2+=4,0,0);
            GPalette[StrobeColor2*3]=StrobeVal2-4;
            }
        }
    else {
        if( LineState = WILL_KILL ) LineState = BEING_BUILT;

        StrobeVal1=0;
        SPal(StrobeColor1,StrobeR1,StrobeG1,StrobeB1);
        StrobeVal2=0;
        SPal(StrobeColor2,StrobeR1,StrobeG2,StrobeB2);
        StrobeSound=0;
        }

    return(gover);
}

//---------------------------------------------------------------------------
// Bonus 5000 and 10000 functions
//---------------------------------------------------------------------------

void SetStar(WORD x,WORD y,WORD dx,WORD dy)
{
    UWORD snew;
    SPRITE *s;

    snew=SPRITEGetFree();

    s=&GSprite[snew];
    s->active=TRUE;
    s->id=LINEBONUS;
    s->x=x; s->y=y;
    s->fly=TRUE;
    s->dline=SPRLINE;
    s->sline=2;
    s->pline=5;
    s->clag=65000;
    s->update=TRUE;
    SPRITENewAnim(snew,4,0);
    WorkoutLine(s,s->x,s->y,dx,dy);
}

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

void Bonus5000()
{
    UWORD snew;
    SPRITE *s;

    GBScore+=5000;

    snew=SPRITEGetFree();
    s=&GSprite[snew];
    s->active=TRUE;
    s->id=LINEBONUS;
    s->x=288; s->y=208;
    SPRITENewAnim(snew,2,0);

    snew=SPRITEGetFree();
    s=&GSprite[snew];
    s->active=TRUE;
    s->id=LINEBONUS;
    s->x=288+32; s->y=208;
    SPRITENewAnim(snew,3,0);

    SetStar(256,208,256-48,208);
    SetStar(352,208,352+48,208);

    SetStar(272+8,176+8,(272-48)+12,(176-48)+12);
    SetStar(272+8,240-8,(272-48)+12,(240+48)-12);

    SetStar(304,176,304,176-48);
    SetStar(304,240,304,240+48);

    SetStar(336-8,176+8,(336+48)-12,(176-48)+12);
    SetStar(336-8,240-8,(336+48)-12,(240+48)-12);
    SoundPlay(10);
}

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

void Bonus10000()
{
    UWORD snew;
    SPRITE *s;

    GBScore+=10000;

    snew=SPRITEGetFree();
    s=&GSprite[snew];
    s->active=TRUE;
    s->id=LINEBONUS;
    s->x=288; s->y=208;
    SPRITENewAnim(snew,0,0);

    snew=SPRITEGetFree();
    s=&GSprite[snew];
    s->active=TRUE;
    s->id=LINEBONUS;
    s->x=288+32; s->y=208;
    SPRITENewAnim(snew,1,0);

    SetStar(256,208,256-48,208);
    SetStar(352,208,352+48,208);

    SetStar(272+8,176+8,(272-48)+12,(176-48)+12);
    SetStar(272+8,240-8,(272-48)+12,(240+48)-12);

    SetStar(304,176,304,176-48);
    SetStar(304,240,304,240+48);

    SetStar(336-8,176+8,(336+48)-12,(176-48)+12);
    SetStar(336-8,240-8,(336+48)-12,(240+48)-12);
    SoundPlay(10);
}


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

// Create a score sprite based on current line accumulation and fly it

void ScoreFly(WORD x,WORD y)
{
    SPRITE *fl;
    UWORD FlyScr;

    if(DetailLevel>0) return;

    FlyScr=SPRITEGetFree();
    fl=&GSprite[FlyScr];
    fl->id=SCORE100+Accum-1;
    fl->walk=FALSE;
    fl->fly=TRUE;
    fl->line=-1;
    fl->xmov=0;
    fl->ymov=0;
    fl->oldx=0;
    fl->oldy=0;
    fl->dline=SPRLINEKILL;
    fl->sline=1;
    fl->pline=10;
    fl->clag=65000;
    fl->active=TRUE;
    fl->update=TRUE;
    fl->x=x;
    fl->y=y;
    WorkoutLine(&GSprite[FlyScr],GSprite[FlyScr].x,GSprite[FlyScr].y,176,40);
    SPRITENewAnim(FlyScr,0,0);
}

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

// Pause game option

BOOL CheckPause()
{
    if( NET_Game != 0 ) return( FALSE );

    if( KEY[K_PAUSE] == TRUE ) return( TRUE );
    return( FALSE );
}

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

void Event_Pause()
{
    UWORD p[10],i;
    SPRITE *s;
    UWORD *prisave;
    void GControlPlayer();

    if( ! EVENT_Pause ) return;
    if( GPlayerMode != INMIDDLE ) return;

    while(GPlayerMode!=INMIDDLE) {
        GRAPHICUpdate();
        GControlPlayer();
        }

    FireEnableStatus=FALSE;

    while( WalkLine != -1 ) {
      GControlPlayer();
    MonitorWalking();
        PANELUpdate();
        GRAPHICUpdate();
        }

    for(i=0;i<10;i++) {
        p[i]=SPRITEGetFree();
        GSprite[p[i]].active=TRUE;
        GSprite[p[i]].id=PAUSED;
        GSprite[p[i]].xmov=0;
        GSprite[p[i]].ymov=0;
        GSprite[p[i]].oldx=0;
        GSprite[p[i]].oldy=0;

        if(i<5) {
        GSprite[p[i]].x=240+(i*32);
        GSprite[p[i]].y=192;
        }
        else {
        GSprite[p[i]].x=240+((i-5)*32);
        GSprite[p[i]].y=192+32;
        }

        SPRITENewAnim(p[i],i,0);
        }

    SetPauseTime();

    GMorphPal(128,192,8,(UBYTE *) WPalette);  //Strobe while flash

    // Save priorities
    prisave = new UWORD[NUMSPRITES];
    s = &GSprite[0];
    for( i=0; i<NUMSPRITES; i++ ) {
        prisave[i] = s->pri;
        if( s->active == TRUE && (s->id < DIGIT0 || s->id == BONUSB || (s->id > DIGIT9 && s->id < BONUSB ) ) ) {
            s->pri = 99; //Not visible
            s->forcedupdate = TRUE;
            }
        s++;
        }

    GRAPHICUpdate();
    GRAPHICUpdate();

    GMorphPal(0,255,8,(UBYTE *) BackPcx.palette);

    GRAPHICUpdate();
    GRAPHICUpdate();
    while(CheckPause()==TRUE);

    while(CheckPause()!=TRUE) {
        WaitTimer();
        NET_Update();
        }
    while(CheckPause()==TRUE) {
        WaitTimer();
        NET_Update();
        }

    for(i=0;i<10;i++) {
    SPRITENewAnim(p[i],10,0); //Kill it
        }
    GRAPHICUpdate();
    GRAPHICUpdate();

    // Restore priorities
    s = &GSprite[0];
    for( i=0; i<NUMSPRITES; i++ ) {
        s->pri = prisave[i];
        s->forcedupdate = TRUE;
        s++;
        }
    delete prisave;

    GRAPHICUpdate();
    GMorphPal(128,192,8,(UBYTE *) WPalette);  //Strobe while flash

    GRAPHICUpdate();

    GMorphPal(0,255,8,(UBYTE *) BackPcx.palette);

    ModifyGameClock();

    FireEnableStatus=TRUE;
    EVENT_Pause = FALSE;
}

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

//--New code: Standard box collision 11/9 jj----------------

inline BOOL BoxCol( DWORD b1xl, DWORD b1yl, DWORD b1xh, DWORD b1yh, DWORD b2xl, DWORD b2yl, DWORD b2xh, DWORD b2yh )
{
    if( b1yl > b2yh ) return( FALSE );
    if( b1yh < b2yl ) return( FALSE );
    if( b1xl > b2xh ) return( FALSE );
    if( b1xh < b2xl ) return( FALSE );

    return( TRUE );
}

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

// Bomb1 - Area bomb, destroys upto 9 enemies surrounding it.
// Also, if bomb1 hits other bomb1's they blow up also!

//--New code: ExplodeBomb generally optimized and debugged 11/9 jj-----------

void ExplodeBomb1(WORD sx,WORD sy)
{
    void BonusRegister(UWORD,UWORD);
    SPRITE *s,*e;
    s=&GSprite[0];
    e=&GSprite[1];
    BOOL EXP;

    WORD behind;
    WORD ex,ey,exx,eyy;

    DWORD b1yl, b1xl, b1yh, b1xh, b2yl, b2xl, b2yh, b2xh;

    b1yl = sy - 20; b1yh = b1yl + 70;
    b1xl = sx - 20; b1xh = b1xl + 70;

    for(UWORD i=1;i<NUMSPRITES;i++) {
        if((e->active == TRUE) && (e->fly == FALSE) && ( ( e->id < EXPLODE ) || ( e->id == BONUSB ) ) ) {
        EXP=FALSE;
        ex=e->x; ey=e->y; exx=e->x+32; eyy=e->y+32;

            EXP = BoxCol( b1xl, b1yl, b1xh, b1yh, ex, ey, exx, eyy );

        if(EXP==TRUE) {
                if(e->id==BOMB1) {
                    e->id=EXPLODE;
                    ExplodeBomb1(ex,ey);
                    }
                if(e->id==BONUSB) {
                BonusRegister(e->x,e->y);
                }
                Kills--;
                GBKills++;
                TotalKills++;
                Accum=1;
                ScoreFly(e->x,e->y);
                GBScore+=100;
                e->id=EXPLODE;
                e->walk=FALSE;
                e->oldx=0;
                e->oldy=0;
                e->xmov=0;
                e->ymov=0;
                SPRITENewAnim(i,0,0);
                LastLineKill=e->line;
                if(e->line==HoldLine) {
                    if(AppearCount > 16) AppearCount=16;
                    }

                e->line=-1;
                }
        }
        e++;
        }
}

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

// Bomb2 - Line bomb, destroys entire line regardless of colors

void ExplodeBomb2(WORD cl)
{
    void BonusRegister(UWORD,UWORD);
    SPRITE *e;
    e=&GSprite[cl];

    if(e->id==BONUSB) {
        BonusRegister(e->x,e->y);
        }
    Kills--;
    GBKills++;
    TotalKills++;
    LastBombKill=e->id;

    Accum=1;

    //Check for color accumulation
    UWORD q=e->quad;
   LastKill=e->id;
   LastLineKill=e->line;
   ScoreFly(e->x,e->y);
   e->id=EXPLODE;
   e->walk=FALSE;
   e->oldx=0;
   e->oldy=0;
   e->xmov=0;
   e->ymov=0;
   SPRITENewAnim(cl,0,0);
   GBScore+=100;

    if(e->line==HoldLine) {
        if(AppearCount > 16) AppearCount=16;
        }

    e->line=-1;
}

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

// Bomb3 - Color bomb, destroys all enemies in quadrant with same color
// as the enemy that this bomb first hits.

void ExplodeBomb3(WORD q,WORD id)
{
    SPRITE *e;
    e=&GSprite[1];

    UWORD ex,ey;

    for(UWORD i=1;i<NUMSPRITES;i++) {
        if(e->active==TRUE && (e->id==id && e->quad==q)) {
        if(e->id==BOMB1 || e->id==BOMB2 || e->id==BOMB3) {
                if(e->id==BOMB1) {
                    e->id=0;
                    ex=e->x; ey=e->y;
                    ExplodeBomb1(ex,ey);
                    }
                }
        Kills--;
        GBKills++;
            TotalKills++;
        Accum=1;
        ScoreFly(e->x,e->y);
        e->id=EXPLODE;
        e->walk=FALSE;
        e->oldx=0;
        e->oldy=0;
        e->xmov=0;
        e->ymov=0;
        SPRITENewAnim(i,0,0);
        GBScore+=100;
        LastLineKill=e->line;
            if(e->line==HoldLine) {
                if(AppearCount > 16) AppearCount=16;
                }
        e->line=-1;
        }
        e++;
        }
}

//===========================================================================
// Add Block functions
//===========================================================================

void AddBlockToLine( DWORD rcolor, DWORD rquad, DWORD rline )
{
    DWORD i,j,k;
    DWORD LineCount,CurrLine;
    WORD Spr,SNew,mx,my,cx,cy,Cline,xpos,ypos;
    BOOL DoneSound;
    SPRITE *s;

    Cline = rquad*4 + rline;

    if(Cline<4) {
        xpos=256+(rline<<5);
        ypos=0;
        mx=0; my=32;
        }
    if(Cline>3 && Cline<8) {
        xpos=608;
        ypos=160+(rline<<5);
        mx=-32; my=0;
        }
    if(Cline>7 && Cline<12) {
        xpos=256+(rline<<5);
        ypos=416;
        mx=0; my=-32;
        //Cline=11-(Cline-8);
        }
    if(Cline>11) {
        xpos=0;
        ypos=160+(rline<<5);
        mx=32; my=0;
        //Cline=15-(Cline-12);
        }

    FindLineCount();

    Spr = FirstOnLine(Cline+1);

    if( Spr != 0 ) {
        xpos = GSprite[Spr].x + mx;
        ypos = GSprite[Spr].y + my;
        }

   SNew = SPRITEGetFree();
    s=&GSprite[SNew];
   s->active=TRUE;
   s->x = xpos;
   s->y = ypos;
   s->quad = rquad;
   s->line = Cline + 1;
   s->id=rcolor;
   s->clag=6;
   s->walk=FALSE;
   s->fly=FALSE;

   if(rquad==1) rquad=3;
   else if(rquad==3) rquad=1;
   SPRITENewAnim(SNew,rquad,1);
}

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

void NETShowReceived( DWORD Target )
{
    UWORD spr;
    SPRITE *s;

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETCROSS + Target;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = 464;
    s->y = 368;
    s->active=TRUE;
    SPRITENewAnim( spr,4,0 );

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETCROSS + Target;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = 464+32;
    s->y = 368;
    s->active=TRUE;
    SPRITENewAnim( spr,5,0 );

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETCROSS + Target;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = 464;
    s->y = 368+32;
    s->active=TRUE;
    SPRITENewAnim( spr,6,0 );

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETCROSS + Target;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = 464+32;
    s->y = 368+32;
    s->active=TRUE;
    SPRITENewAnim( spr,7,0 );
}

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

void AddBlock( DWORD rcolor, DWORD nblocks )
{
    void GControlPlayer();
    void ScoreTrack();
    void KillsTrack();
    DWORD q,r,l,maxl,i,j;

    SPRITE *s;
    UWORD b,zc;
    BOOL DoKill, DoFly, FoundLine;

    if( GPlayerMode != INMIDDLE ) return;
    if( LineState == BEING_BUILT ) return;

    NETShowReceived( 0 );

    FireEnableStatus=FALSE;

    for( i=0; i<32; i++ ) {
      GControlPlayer();
    MonitorWalking();
        PANELUpdate();
        GRAPHICUpdate();
        }

    // Wait for beam to terminate
    DoKill=TRUE;
    while(DoKill==TRUE) {
      GControlPlayer();
        PANELUpdate();
        GRAPHICUpdate();
        DoKill=FALSE;
        for(i=1;i<NUMSPRITES;i++) {
        if((GSprite[i].active==TRUE) && (GSprite[i].id==LIGHTBEAM)) DoKill=TRUE;
        }
        }

    // Wait for flying sprites to finish
    DoFly=TRUE;
    while(DoFly==TRUE) {
      GControlPlayer();
        PANELUpdate();
        GRAPHICUpdate();
        DoFly=FALSE;
        for(i=1;i<NUMSPRITES;i++) {
        if((GSprite[i].active==TRUE) && (GSprite[i].dline!=NOLINE)) DoFly=TRUE;
        }
        }

    GMorphPal(128,192,8,(UBYTE *) WPalette);  //Strobe while flash

    for( i=0; i<nblocks; i++ ) {
        FindLineCount();

        q = RandQuad();
        r = RandLine();
        l = q*4 + r;
        if( q == 0 || q == 2 ) maxl = 3;  //top or bottom
        else maxl = 6; //left or right

        FoundLine = TRUE;
        if( GLine[ l ].count >= maxl ) {  //Line to full for additions
            FoundLine = FALSE;
            for( j=l+1; j!=l; j = (j+1) & 15 ) {
                q = j / 4;
                r = j % 4;
                if( q == 0 || q == 2 ) maxl = 3;  //top or bottom
                else maxl = 6; //left or right

                if( GLine[ j ].count < maxl ) {
                    FoundLine = TRUE;
                    break;  //Found room in line
                    }
                }
            if( j == l ) {
                FoundLine = FALSE;
                break;  //No room left
                }
            }

        if( FoundLine ) AddBlockToLine( rcolor, q, r );
        }

    GMorphPal(0,255,8,(UBYTE *) BackPcx.palette);

    FireEnableStatus=TRUE;

    EVENT_AddBlock--;
}

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

void Event_AddBlock()
{
    if( EVENT_AddBlock > 0 ) {
        BlockAddColor = ( rand() & 3 );
        AddBlock( BlockAddColor, BlockAddAmount );
        }
}

//===========================================================================
// Bonus event functions
//===========================================================================

void SpringSetup()
{
    for(UWORD i=0;i<5;i++)
        Spring[i]=0;
}

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

// Check if bonus is complete and if so, initiate enemy clear sequence

void DoSingleBonus()
{
    void GControlPlayer();
    void ScoreTrack();
    void KillsTrack();
    DWORD SoundPause;
    SPRITE *s;
    UWORD b,i,zc;
    BOOL DoKill, DoFly;

    if( GPlayerMode != INMIDDLE ) return;
    if( LineState == BEING_BUILT ) return;

    FireEnableStatus=FALSE;

    for( i=0; i<32; i++ ) {
      GControlPlayer();
    MonitorWalking();
        PANELUpdate();
        GRAPHICUpdate();
        }

    // Wait for beam to terminate
    DoKill=TRUE;
    while(DoKill==TRUE) {
      GControlPlayer();
        PANELUpdate();
        GRAPHICUpdate();
        DoKill=FALSE;
        for(i=1;i<NUMSPRITES;i++) {
        if((GSprite[i].active==TRUE) && (GSprite[i].id==LIGHTBEAM)) DoKill=TRUE;
        }
        }

    // Wait for flying sprites to finish
    DoFly=TRUE;
    while(DoFly==TRUE) {
      GControlPlayer();
        PANELUpdate();
        GRAPHICUpdate();
        DoFly=FALSE;
        for(i=1;i<NUMSPRITES;i++) {
        if((GSprite[i].active==TRUE) && (GSprite[i].dline!=NOLINE)) DoFly=TRUE;
        }
        }

    for(i=0;i<5;i++) {
        SPRITENewAnim(Spring[i],3 + (NET_Game*5),1);
        }

    //--New code: Remove strobe 8/9 jj-------------------------------------------
    if( NEWspr != -1 )
    {
        s = &GSprite[ NEWspr ];

        s->id=0;
        s->walk=FALSE;
        s->fly=FALSE;
        s->line=-1;
        s->xmov=0;
        s->ymov=0;
        s->oldx=0;
        s->oldy=0;
        s->clag=0;
        s->spawnid=-1;
        s->active=FALSE;
        NEWspr=-1;
        LineState = BUILT;

        AppearCount = 0;
    }
    //---------------------------------------------------------------------------


    for(i=0;i<30;i++) {
        PANELUpdate();
        GRAPHICUpdate();
        }
    SoundPause=1;

    GBScore+=10000;

    GSprite[0].animtog=FALSE;

    GMorphPal(128,192,8,(UBYTE *) WPalette);  //Strobe while flash
    GMorphPal(0,255,8,(UBYTE *) BackPcx.palette);

    UWORD lk;
    UWORD Sel[NUMSPRITES];
    UWORD NSel;
    UBYTE r1,r2;

    r1=rand() & 0xff;
    while (r1==0) r1=rand() & 0xff;
    r2=rand() & 16;
    while (r2==0) r2=rand() & 16;

    NSel=1;
    while(NSel!=0) {
        NSel=0;
        s=&GSprite[1];
        for(i=1;i<NUMSPRITES;i++) {
        if(s->active==TRUE && (s->id<EXPLODE)) {
                Sel[NSel++]=i;
                }
        s++;
        }
        if(NSel!=0) {
        r1+=r2;
        lk=r1 & 0xff;
        while(lk>=NSel) lk=0;
        lk=Sel[lk];

        Kills--;
        GBKills++;
            TotalKills++;
        s=&GSprite[lk];
        s->id=EXPLODE;
        s->walk=FALSE;
        s->oldx=0;
        s->oldy=0;
        s->xmov=0;
        s->ymov=0;
        SPRITENewAnim(lk,0,0);
        s->animtime=2;
        GBScore+=100;
            SoundPause--;
            if(SoundPause<1) {
                SoundPlay(10);
                SoundPause=2;
                }
        for(i=0;i<3;i++) {
                PANELUpdate();
                GRAPHICUpdate();
                }
        }
        }
    for(i=0;i<16;i++) {
        PANELUpdate();
        GRAPHICUpdate();
        }

    BOOL DoScup=TRUE;

    while(DoScup==TRUE) {
        PANELUpdate();
        GRAPHICUpdate();
        for(i=0;i<7;i++) {
        if(GSprite[Score[i]].animtog==TRUE) DoScup=TRUE;
        else DoScup=FALSE;
        }
        }

    LastKill=0;
    LastLineKill=0;

    GMorphPal(128,192,8,(UBYTE *) WPalette);  //Strobe while flash
    GMorphPal(0,255,6,(UBYTE *) BackPcx.palette);

    for(i=0;i<5;i++) {
        GSprite[Spring[i]].id=BONUSB;
        SPRITENewAnim(Spring[i],4 + (NET_Game * 5),1);
        PANELUpdate();
        GRAPHICUpdate();
        PANELUpdate();
        GRAPHICUpdate();
        PANELUpdate();
        GRAPHICUpdate();
        }

    for(i=0;i<30;i++) {
        PANELUpdate();
        GRAPHICUpdate();
        }

    SpringSetup();
    SpringFull=0;

    if( Kills < 1 ) LineState = BUILT;  // This will enable instant trigger of next level sequence
    else LineState = BEING_BUILT;

    AppearCount = 0;

    FireEnableStatus=TRUE;

    EVENT_Bonus--;
}

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

void NETMergeTargets()
{
    SPRITE *s;
    UWORD i;

    //Check for flying springs
    for( i=0; i<5; i++ )
    {
        s = &GSprite[ SpringMerge[ i ] ];
        if( s->dline != NOLINE ) return;
    }

    s = &GSprite[ SpringMerge[0] ];
    s->fly=TRUE;
    s->dline=SPRLINE;
    s->sline=1;
    s->pline=20;
    WorkoutLine( s, s->x, s->y, s->x + 64, s->y );
    if(DetailLevel==0) {
        s->spawnid=TRAIL;
        s->spawntime=2;
        s->spawncnt=2;
        }
    else s->spawnid=-1;

    s = &GSprite[ SpringMerge[1] ];
    s->fly=TRUE;
    s->dline=SPRLINE;
    s->sline=1;
    s->pline=10;
    WorkoutLine( s, s->x, s->y, s->x + 32, s->y );
    if(DetailLevel==0) {
        s->spawnid=TRAIL;
        s->spawntime=2;
        s->spawncnt=2;
        }
    else s->spawnid=-1;

    s = &GSprite[ SpringMerge[4] ];
    s->fly=TRUE;
    s->dline=SPRLINE;
    s->sline=1;
    s->pline=20;
    WorkoutLine( s, s->x, s->y, s->x - 64, s->y );
    if(DetailLevel==0) {
        s->spawnid=TRAIL;
        s->spawntime=2;
        s->spawncnt=2;
        }
    else s->spawnid=-1;

    s = &GSprite[ SpringMerge[3] ];
    s->fly=TRUE;
    s->dline=SPRLINE;
    s->sline=1;
    s->pline=10;
    WorkoutLine( s, s->x, s->y, s->x - 32, s->y );
    if(DetailLevel==0) {
        s->spawnid=TRAIL;
        s->spawntime=2;
        s->spawncnt=2;
        }
    else s->spawnid=-1;

    NETSEQUENCE++;
    NETSEQUENCECOUNT = 60;
}

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

DWORD MidSprite;

void NETChooseType()
{
    SPRITE *s;
    DWORD r,spr,i;

    if( NETSEQUENCECOUNT != 0 ) {
        NETSEQUENCECOUNT--;
        return;
        }

    for(i=0;i<5;i++) {
        GSprite[SpringMerge[i]].id=BONUSB;
        SPRITENewAnim(SpringMerge[i],4 + (NET_Game * 5),1);
        }

    r = rand() & 31;
    if( r < 11 ) r = 0;
    else {
        if( r < 21 ) r = 1;
        else r = 2;
        }

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETCROSSFLASH + r;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = 64;
    s->y = 384;
    s->active=TRUE;
    SPRITENewAnim( spr,0,0 );

    s = &GSprite[ SpringMerge[ 2 ] ];
    s->active = FALSE;

    MidSprite = spr;

    NETTARGETTYPE = r;

    NETSEQUENCE++;
    NETSEQUENCECOUNT = 80;
}

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

void NETChooseSend()
{
    UWORD spr;
    SPRITE *s;

    if( NETSEQUENCECOUNT != 0 ) {
        NETSEQUENCECOUNT--;
        return;
        }

    SPRITENewAnim( MidSprite,1,0 );

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETCROSS + NETTARGETTYPE;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = 48;
    s->y = 368;
    s->active=TRUE;
    SPRITENewAnim( spr,0,0 );

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETCROSS + NETTARGETTYPE;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = 48+32;
    s->y = 368;
    s->active=TRUE;
    SPRITENewAnim( spr,1,0 );

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETCROSS + NETTARGETTYPE;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = 48;
    s->y = 368+32;
    s->active=TRUE;
    SPRITENewAnim( spr,2,0 );

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETCROSS + NETTARGETTYPE;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = 48+32;
    s->y = 368+32;
    s->active=TRUE;
    SPRITENewAnim( spr,3,0 );

    NETSEQUENCE++;
    NETSEQUENCECOUNT = 60;
}

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

void NETSendArrows( DWORD send )
{
    DWORD pos, xp;
    UWORD spr;
    SPRITE *s,*fs;

    pos = ( send + 1 ) * 5;
    xp = NET_Name_Posx[ pos ];

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETFONT;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = xp - 16;
    s->y = 448;
    s->active=TRUE;
    SPRITENewAnim( spr, 49, 0 );

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETFONT;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = xp + ( 16 * 5 );
    s->y = 448;
    s->active=TRUE;
    SPRITENewAnim( spr, 48, 0 );
}

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

void NETReceiveArrows( DWORD send )
{
    DWORD pos, xp;
    UWORD spr;
    SPRITE *s,*fs;

    pos = ( send + 1 ) * 5;
    xp = NET_Name_Posx[ pos ];

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETFONT;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = xp - 32;
    s->y = 448;
    s->active=TRUE;
    SPRITENewAnim( spr, 48, 0 );

    spr = SPRITEGetFree();
    s = &GSprite[ spr ];
    s->id = NETFONT;
    s->line=-1;
    s->walk=FALSE;
    s->fly=FALSE;
    s->oldx=0;
    s->oldy=0;
    s->xmov=0;
    s->ymov=0;
    s->x = xp + ( 16 * 5 ) + 16;
    s->y = 448;
    s->active=TRUE;
    SPRITENewAnim( spr, 49, 0 );
}

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

void NETSendToPlayer()
{
    DWORD Send;

    if( NETSEQUENCECOUNT != 0 ) {
        NETSEQUENCECOUNT--;
        return;
        }

    Send = NETRandomPlayer();

    if( Send != -1 ) {
        NETSendArrows( Send );
        NETSendEvent( Send, 'A' + NETTARGETTYPE, 8 ); //Block count will also go here
        }

    NETSEQUENCE++;
}

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

void DoNetBonus()
{
    void GControlPlayer();
    void ScoreTrack();
    void KillsTrack();
    DWORD SoundPause;
    SPRITE *s;
    UWORD b,i,zc;
    BOOL DoKill, DoFly;

    switch( NETSEQUENCE )
    {
        case 0:
            NETMergeTargets();
            break;
        case 1:
            NETChooseType();
            break;
        case 2:
            NETChooseSend();
            break;
        case 3:
            NETSendToPlayer();
            break;
        case 4:
            if( NETSEQUENCECOUNT == 0 ) {
                EVENT_Bonus--;
                }
            else NETSEQUENCECOUNT--;
            break;
    }
}

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

void DoBlowUpBonus()
{
    void GControlPlayer();
    void ScoreTrack();
    void KillsTrack();
    DWORD SoundPause;
    SPRITE *s;
    UWORD b,i,zc;
    BOOL DoKill, DoFly;

    if( GPlayerMode != INMIDDLE ) return;
    if( LineState == BEING_BUILT ) return;

    NETShowReceived( 1 );

    FireEnableStatus=FALSE;

    for( i=0; i<32; i++ ) {
      GControlPlayer();
    MonitorWalking();
        PANELUpdate();
        GRAPHICUpdate();
        }

    // Wait for beam to terminate
    DoKill=TRUE;
    while(DoKill==TRUE) {
      GControlPlayer();
        PANELUpdate();
        GRAPHICUpdate();
        DoKill=FALSE;
        for(i=1;i<NUMSPRITES;i++) {
        if((GSprite[i].active==TRUE) && (GSprite[i].id==LIGHTBEAM)) DoKill=TRUE;
        }
        }

    // Wait for flying sprites to finish
    DoFly=TRUE;
    while(DoFly==TRUE) {
      GControlPlayer();
        PANELUpdate();
        GRAPHICUpdate();
        DoFly=FALSE;
        for(i=1;i<NUMSPRITES;i++) {
        if((GSprite[i].active==TRUE) && (GSprite[i].dline!=NOLINE)) DoFly=TRUE;
        }
        }

    for(i=0;i<30;i++) {
        PANELUpdate();
        GRAPHICUpdate();
        }
    SoundPause=1;

    GBScore+=10000;

    GSprite[0].animtog=FALSE;

    GMorphPal(128,192,8,(UBYTE *) WPalette);  //Strobe while flash
    GMorphPal(0,255,8,(UBYTE *) BackPcx.palette);

    UWORD lk;
    UWORD Sel[NUMSPRITES];
    UWORD NSel;
    UBYTE r1,r2;

    r1=rand() & 0xff;
    while (r1==0) r1=rand() & 0xff;
    r2=rand() & 16;
    while (r2==0) r2=rand() & 16;

    NSel=1;
    while(NSel!=0) {
        NSel=0;
        s=&GSprite[1];
        for(i=1;i<NUMSPRITES;i++) {
        if(s->active==TRUE && (s->id<EXPLODE || s->id==BONUSB)) {
                Sel[NSel++]=i;
                }
        s++;
        }
        if(NSel!=0) {
        r1+=r2;
        lk=r1 & 0xff;
        while(lk>=NSel) lk=0;
        lk=Sel[lk];

        Kills--;
        GBKills++;
            TotalKills++;
        s=&GSprite[lk];
        s->id=EXPLODE;
        s->walk=FALSE;
        s->oldx=0;
        s->oldy=0;
        s->xmov=0;
        s->ymov=0;
        SPRITENewAnim(lk,0,0);
        s->animtime=2;
        GBScore+=100;
            SoundPause--;
            if(SoundPause<1) {
                SoundPlay(10);
                SoundPause=2;
                }
        for(i=0;i<3;i++) {
                PANELUpdate();
                GRAPHICUpdate();
                }
        }
        }
    for(i=0;i<16;i++) {
        PANELUpdate();
        GRAPHICUpdate();
        }

    BOOL DoScup=TRUE;

    while(DoScup==TRUE) {
        PANELUpdate();
        GRAPHICUpdate();
        for(i=0;i<7;i++) {
        if(GSprite[Score[i]].animtog==TRUE) DoScup=TRUE;
        else DoScup=FALSE;
        }
        }

    LastKill=0;
    LastLineKill=0;

    GMorphPal(128,192,8,(UBYTE *) WPalette);  //Strobe while flash
    GMorphPal(0,255,6,(UBYTE *) BackPcx.palette);

    if( Kills < 1 ) LineState = BUILT;  // This will enable instant trigger of next level sequence
    else LineState = BEING_BUILT;

    AppearCount = 0;

    FireEnableStatus=TRUE;

    EVENT_BlowUp--;
}

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

void DoAddTarget()
{
    SPRITE *rs;
    UWORD fs, i;

    for( i=0; i<SpringFull; i++ )
    {
        rs = &GSprite[ Spring[ i ] ];
        if( rs->dline != NOLINE ) return;
    }

    SoundPlay(9);

    fs=SPRITEGetFree();
    rs=&GSprite[fs];

    rs->id=BONUSO;
    rs->walk=FALSE;
    rs->line=-1;
    rs->xmov=0;
    rs->ymov=0;
    rs->oldx=0;
    rs->oldy=0;
    rs->active=TRUE;
    rs->update=TRUE;
    rs->clag=65000;
    rs->fly=FALSE;
    rs->x=SpringFull*32;
    rs->y=12*32;
    rs->spawnid=-1;

    SPRITENewAnim(fs,(NET_Game * 5)+1,0);

    Spring[SpringFull]=fs;
    SpringFull++;

    fs=SPRITEGetFree();
    rs=&GSprite[fs];

    rs->id=TRAIL;
    rs->walk=FALSE;
    rs->line=-1;
    rs->xmov=0;
    rs->ymov=0;
    rs->oldx=0;
    rs->oldy=0;
    rs->active=TRUE;
    rs->update=TRUE;
    rs->clag=65000;
    rs->fly=FALSE;
    rs->x=(SpringFull-1)*32;
    rs->y=12*32;
    rs->spawnid=-1;

    SPRITENewAnim(fs,0,0);

    if(SpringFull >= 5) {
        for( i=0; i<5; i++ ) {
            rs = &GSprite[ Spring[ i ] ];
            if( rs->dline != SPRLINE ) {
                rs->id = EXPLODE;
                SPRITENewAnim( Spring[i],0,0 );
                SpringFull--;
                }
            }
        }
    EVENT_AddTarget--;
}

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

void Event_BlowUp()
{
    if( EVENT_BlowUp != 0 ) DoBlowUpBonus();
}

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

void Event_AddTarget()
{
    if( EVENT_AddTarget != 0 ) DoAddTarget();
}

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

void Event_Bonus()
{
    if( EVENT_Bonus != 0 ) {
        if( NET_Game == 0 ) DoSingleBonus();
        else DoNetBonus();
        }
}

//===========================================================================
// SpeedUp event functions
//===========================================================================

void DoSpeedUp()
{
    SpeedUpDuration--;
    if( SpeedUpDuration == 0 ) TimeSpeed = OldSpeed;
}

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

void Event_SpeedUp()
{
    if( EVENT_SpeedUp != 0 && SpeedUpDuration == 0 ) {
        NETShowReceived( 2 );
        SpeedUpDuration = 300;
        OldSpeed = TimeSpeed;
        TimeSpeed = 1;
        EVENT_SpeedUp--;
        }
    if( SpeedUpDuration != 0 ) DoSpeedUp();
}

//===========================================================================
// Register collection of bonus spring
//===========================================================================

void BonusRegister(UWORD x,UWORD y)
{
    SPRITE *rs;
    UWORD fs,i;

    SoundPlay(9);

    fs=SPRITEGetFree();
    rs=&GSprite[fs];

    //Set letter to fly
    rs->id=BONUSO;
    rs->walk=FALSE;
    rs->line=-1;
    rs->xmov=0;
    rs->ymov=0;
    rs->oldx=0;
    rs->oldy=0;
    rs->active=TRUE;
    rs->update=TRUE;
    rs->dline=SPRLINE;
    rs->sline=1;
    rs->pline=0;
    rs->clag=65000;
    rs->fly=TRUE;
    rs->x=x;
    rs->y=y;

    if(DetailLevel==0) {
        rs->spawnid=TRAIL;
        rs->spawntime=2;
        rs->spawncnt=2;
        }
    else rs->spawnid=-1;

    WorkoutLine(rs,x,y,(SpringFull*32),(12*32));
    SPRITENewAnim(fs,(NET_Game * 5),0);

    Spring[SpringFull]=fs;
    SpringFull++;

    if(SpringFull == 5) {
        NETSEQUENCE = 0;
        EVENT_Bonus++;
        for( i=0; i<5; i++ ) {
            SpringMerge[ i ] = Spring[ i ];
            }
        SpringFull = 0;
        }

    NETSendAllPlayers( 'D', 0 );
}

//===========================================================================
// Player functions
//===========================================================================

// Check player collisions, deals with colors,bombs and line bonus

UWORD GPlayerCollision()
{
    UWORD cl;
    WORD HitSpr;
    SPRITE *pl=&GSprite[0],*hit;

    DoBeam=TRUE;

    switch(GPlayerFacing) {
        case FACEUP:
        cl=CheckCollision(GPlayerRunLine,0,-4);
        break;
        case FACEDOWN:
        cl=CheckCollision(GPlayerRunLine,0,4);
        break;
        case FACELEFT:
        cl=CheckCollision(GPlayerRunLine,-4,0);
        break;
        case FACERIGHT:
        cl=CheckCollision(GPlayerRunLine,4,0);
        break;
        }
    if(cl!=0 && GSprite[cl].id==BONUSB) {
        Accum=1;
        GBScore+=100;
        LastKill=-1;
        Kills--;
        GBKills++;
        TotalKills++;
        BonusRegister(GSprite[cl].x,GSprite[cl].y);
//--New code: Spring now creates 100 sprite 12/9 jj----------------
        hit=&GSprite[cl];
        ScoreFly(hit->x,hit->y);
        hit->id=0;
        hit->active=FALSE;
        hit->line=-1;
        hit->walk=FALSE;
        hit->oldx=0;
        hit->oldy=0;
        hit->xmov=0;
        hit->ymov=0;
        cl=0;
        }

    if(cl!=0) {
        UWORD PID=pl->id;

        if(PID>=PCOLOR1 && PID<=PCOLOR4) PID-=PCOLOR1;
        if(GSprite[cl].id!=PID || pl->id==BOMB1 || pl->id==BOMB2 || pl->id==BOMB3) {
        UWORD rquad=GSprite[cl].quad;

//--New code: Also added pl->id != BOMB1 because BOMB1 now kills hit icon 11/9 jj----------------
        if(GSprite[cl].id<=COLOR4 && GSprite[cl].id!=PID && pl->id!=BOMB2 && pl->id != BOMB1 ) {
                HitSpr=SPRITEGetFree();
                hit=&GSprite[HitSpr];
                hit->id=COLORHIT1+GSprite[cl].id;
                hit->walk=FALSE;
                hit->fly=FALSE;
                hit->line=-1;
                hit->xmov=0;
                hit->ymov=0;
                hit->oldx=0;
                hit->oldy=0;
                hit->active=TRUE;
                hit->update=TRUE;
                hit->x=GSprite[cl].x;
                hit->y=GSprite[cl].y;
                SPRITENewAnim(HitSpr,0,0);
                if(rquad==0) GSprite[HitSpr].y+=16;
                if(rquad==1) GSprite[HitSpr].x-=16;
                if(rquad==2) GSprite[HitSpr].y-=16;
                if(rquad==3) GSprite[HitSpr].x+=16;
                }

        if(GSprite[cl].clag==0) { // Swap colors with sprite hit
                UWORD tid=GSprite[cl].id;
                GSprite[cl].id=PID;
                pl->id=tid;
                if(tid<BOMB1) pl->id+=PCOLOR1;
                PID=tid;

//--Additional code: Do not generate bomb collect sound if hit with bomb2(linebomb) 12/9 jj----------------
                if(PID==BOMB1 && GSprite[cl].id != BOMB2 ) SoundPlay(8);
                if(PID==BOMB2) SoundPlay(7);
                if(PID==BOMB3 && GSprite[cl].id != BOMB2 ) SoundPlay(6);
                }

        if(GSprite[cl].id==BOMB1) {
                SoundPlay(10);
                GSprite[cl].id=0;
                ExplodeBomb1(GSprite[cl].x,GSprite[cl].y);
                }
        if(GSprite[cl].id==BOMB2) {
                if(Bomb2Sound!=TRUE) {
                    SoundPlay(10);
                    Bomb2Sound=TRUE;
                    }
                GSprite[cl].id=PID;
                pl->id=BOMB2;
                ExplodeBomb2(cl);
                cl=0;
                }
        if(GSprite[cl].id==BOMB3) {
                SoundPlay(10);
                GSprite[cl].id=PID;
                ExplodeBomb3(GSprite[cl].quad,PID);
                }
        if(GSprite[cl].id<=COLOR4) {
                //SoundPlay(7);
                if(rquad==1) rquad=3;
                else if(rquad==3) rquad=1;
                SPRITENewAnim(cl,rquad,1);
                }



        }
        else {
        if(GSprite[cl].id==LastKill && GSprite[cl].line==LastLineKill) {
                Accum++;
                }
        else {
                Accum=1;
                }
            //if(GPlayerRunLine==HoldLine) AppearCount=16+TimeSpeed;

            //Create fly score sprite here
        ScoreFly(GSprite[cl].x,GSprite[cl].y);
        GBScore+=100*Accum;
        LastKill=GSprite[cl].id;
        LastLineKill=GSprite[cl].line;
        Kills--;
        GBKills++;
            TotalKills++;
        UWORD q=GSprite[cl].quad;
            SPRITE *Bon;
            //Check accumulated line for single color clearence
            Bon=&GSprite[cl];
        if(((q==0 || q==2) && Accum==5) || ((q==1 || q==3) && Accum==8)) {
                if(Accum==5) Bonus5000();
                else Bonus10000();
                }
            Bon->id=0;
            Bon->active=FALSE;
        Bon->line=-1;
        Bon->walk=FALSE;
        Bon->oldx=0;
        Bon->oldy=0;
        Bon->xmov=0;
        Bon->ymov=0;

        DoBeam=FALSE;
        cl=0;
        }
        }

    return(cl);
}

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

// Player feedback (direction and firing)

#define PLAYERUP        1
#define PLAYERDOWN  2
#define PLAYERLEFT  3
#define PLAYERRIGHT 4

DWORD JoyLastDir;
DWORD JoyLastMov;

void JoystickResponse( UWORD *d, BOOL *f )
{
    DWORD jx, jy;

    jx = jy = 0;

    if( Joy.JOY1x < ( JoyNormalx - (JoyNormalx/4) ) ) jx = -1;
    if( Joy.JOY1x > ( JoyNormalx + (JoyNormalx/4) ) ) jx = 1;
    if( Joy.JOY1y < ( JoyNormaly - (JoyNormaly/4) ) ) jy = -1;
    if( Joy.JOY1y > ( JoyNormaly + (JoyNormaly/4) ) ) jy = 1;

    if( (Joy.JOYd1 & JOYafire) == JOYafire ) *f = TRUE;
    else *f=FALSE;

    //Continual direction check
    if( jx == 1 && JoyLastMov == PLAYERRIGHT ) {
        *d = PLAYERRIGHT;
        return;
        }

    if( jx == -1 && JoyLastMov == PLAYERLEFT ) {
        *d = PLAYERLEFT;
        return;
        }

    if( jy == 1 && JoyLastMov == PLAYERDOWN ) {
        *d = PLAYERDOWN;
        return;
        }

    if( jy == -1 && JoyLastMov == PLAYERUP ) {
        *d = PLAYERUP;
        return;
        }

    //Diagonal check
    if( jx != 0 && jy != 0 ) {
        switch( JoyLastMov ) {
            case PLAYERUP:
                if( jx == -1 ) *d = PLAYERLEFT;
                if( jx == 1 ) *d = PLAYERRIGHT;
                break;
            case PLAYERDOWN:
                if( jx == -1 ) *d = PLAYERLEFT;
                if( jx == 1 ) *d = PLAYERRIGHT;
                break;
            case PLAYERLEFT:
                if( jy == -1 ) *d = PLAYERUP;
                if( jy == 1 ) *d = PLAYERDOWN;
                break;
            case PLAYERRIGHT:
                if( jy == -1 ) *d = PLAYERUP;
                if( jy == 1 ) *d = PLAYERDOWN;
                break;
            }
        JoyLastMov = *d;
        return;
        }

    //Directional movement
    if( jx == -1 ) *d = PLAYERLEFT;
    if( jx == 1 ) *d = PLAYERRIGHT;
    if( jy == -1 ) *d = PLAYERUP;
    if( jy == 1 ) *d = PLAYERDOWN;

    if( *d != 0) JoyLastMov = *d;
}

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

void MOUSEHide()
{
    union REGS r;

    r.w.ax=0x0002;
    r.w.bx=0x0000;
    r.w.cx=0x0000;
    r.w.dx=0x0000;
    int386(0x33,&r,&r);
}

BOOL MOUSEReset()
{
    union REGS r;

    r.w.ax=0x0000;
    r.w.bx=0x0000;
    r.w.cx=0x0000;
    r.w.dx=0x0000;
    int386(0x33,&r,&r);
    if(r.w.ax==0) return( FALSE ); else return( TRUE );
}

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

// get mouse x,y movement and button status - also checks for double clicks

void MOUSEGet( WORD &Mx, WORD &My, UWORD &Mb )
{
    union REGS r;

    Mb=0;
    r.w.ax=0x0003;
    int386(0x33,&r,&r);
    Mb=r.w.bx;

    Mx=My=0;

    r.w.ax=0x000b;
    int386(0x33,&r,&r);
    Mx=r.w.cx; My=r.w.dx;
}

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

void MouseResponse( UWORD *d, BOOL *f )
{
    WORD mx, my;
    UWORD mb;

    MOUSEGet( mx, my, mb );

    if( abs(mx) > abs(my) ) {   //Find the most pushed in direction
        if( mx < -2 ) *d = PLAYERLEFT;
        if( mx > 2 ) *d = PLAYERRIGHT;
        }
    else {
        if( my < -2 ) *d = PLAYERUP;
        if( my > 2 ) *d = PLAYERDOWN;
        }

    if( (mb & MOUSE_BUTTON_LEFT ) != 0 ) *f = TRUE;
    else *f=FALSE;
}

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

void KeyboardResponse( UWORD *d, BOOL *f )
{
    if(KEY[Key_Up]) *d=PLAYERUP;
    if(*d==0) {
        if(KEY[Key_Down]) *d=PLAYERDOWN;
        if(*d==0) {
        if(KEY[Key_Left]) *d=PLAYERLEFT;
        if(*d==0 && KEY[Key_Right]) *d=PLAYERRIGHT;
        }
        }
    if(KEY[Key_Fire]) *f=TRUE;
    else *f=FALSE;
}

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

void PlayerResponse(UWORD *d,BOOL *f)
{
    //First do default keyboard

    *d=0;

    if(KEY[K_UP]) *d=PLAYERUP;
    if(*d==0) {
        if(KEY[K_DOWN]) *d=PLAYERDOWN;
        if(*d==0) {
        if(KEY[K_LEFT]) *d=PLAYERLEFT;
        if(*d==0 && KEY[K_RIGHT]) *d=PLAYERRIGHT;
        }
        }
    if(KEY[K_FIRE]) *f=TRUE;
    else *f=FALSE;

    if( *d != 0 || *f != 0 ) return; //Player used default

    switch( AuxInput ) {
        case CONTROL_JOYSTICK:
            JoystickResponse( d, f );
            break;
        case CONTROL_MOUSE:
            MouseResponse( d, f );
            break;
        case CONTROL_KEYBOARD:
            KeyboardResponse( d, f );
            break;
        }
}

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

// Main player movement

void GControlPlayer()
{
    SPRITE *Bm;
    WORD BeamSpr,Plx,Ply;
    UWORD cl;
    BOOL Moved=FALSE,DoBeam;
    BOOL Ku=FALSE,Kd=FALSE,Kl=FALSE,Kr=FALSE;
    UWORD Dir;
    BOOL Fire;

    SPRITE *pl=&GSprite[0];

    PlayerResponse(&Dir,&Fire);

    if(FireEnableStatus==FALSE) Fire=FALSE;

    switch(GPlayerMode) {
        case INMIDDLE:
            Accum=1;
            BombTwoFired=FALSE;
            if(Dir==PLAYERUP) {
            if(GPlayerFacing!=FACEUP && (pl->id!=BOMB1 && pl->id!=BOMB2 && pl->id!=BOMB3)) SPRITEAnim(0,AUP,1);
            Moved=TRUE;
            GPlayerFacing=FACEUP;
            if(pl->xcnt==0 && pl->ycnt==0 && pl->y>160) {
                    //SoundPlay(4);
                    pl->ymov=-PlayerSpeed;
                    pl->ycnt=PlayerMoveBy;
                    }
            }
            if(Dir==PLAYERDOWN) {
            if(GPlayerFacing!=FACEDOWN && (pl->id!=BOMB1 && pl->id!=BOMB2 && pl->id!=BOMB3)) SPRITEAnim(0,ADOWN,1);
            GPlayerFacing=FACEDOWN;
            Moved=TRUE;
            if(pl->xcnt==0 && pl->ycnt==0 && pl->y<256) {
                    //SoundPlay(4);
                    pl->ymov=PlayerSpeed;
                    pl->ycnt=PlayerMoveBy;
                    }
            }
            if(Dir==PLAYERLEFT) {
            if(GPlayerFacing!=FACELEFT && (pl->id!=BOMB1 && pl->id!=BOMB2 && pl->id!=BOMB3)) SPRITEAnim(0,ALEFT,1);
            GPlayerFacing=FACELEFT;
            Moved=TRUE;
            if(pl->xcnt==0 && pl->ycnt==0 && pl->x>256) {
                    //SoundPlay(4);
                    pl->xmov=-PlayerSpeed;
                    pl->xcnt=PlayerMoveBy;
                    }
            }
            if(Dir==PLAYERRIGHT) {
            if(GPlayerFacing!=FACERIGHT && (pl->id!=BOMB1 && pl->id!=BOMB2 && pl->id!=BOMB3)) SPRITEAnim(0,ARIGHT,1);
            GPlayerFacing=FACERIGHT;
            Moved=TRUE;
            if(pl->xcnt==0 && pl->ycnt==0 && pl->x<352) {
                    //SoundPlay(4);
                    pl->xmov=PlayerSpeed;
                    pl->xcnt=PlayerMoveBy;
                    }
            }
            if(Fire==TRUE && FireWait==0) {
            if(((pl->x & 31)!=0) || ((pl->y & 31)!=0)) return;
                SoundPlay(11);
            GPlayerMode=RUNTO;
            GStartRunx=pl->x;
            GStartRuny=pl->y;
            if(pl->id==BOMB2) {
                    LastBombKill=BOMB2;
                    BombTwoFired=TRUE;
                    }

            switch(GPlayerFacing) {
                    case FACEUP:
                        GPlayerRunLine=(pl->x >> 5)-7;
                    pl->xmov=0;
                    pl->ymov=-FIRESTEP;
                    pl->xcnt=0;
                        pl->ycnt=40;
                    SPRITEAnim(0,AUP,1);
                        break;
                    case FACEDOWN:
                        GPlayerRunLine=((pl->x >> 5)-7)+8;
                    pl->xmov=0;
                    pl->ymov=FIRESTEP;
                    pl->xcnt=0;
                        pl->ycnt=40;
                    SPRITEAnim(0,ADOWN,1);
                    break;
                    case FACELEFT:
                        GPlayerRunLine=((pl->y >> 5)-4)+12;
                    pl->xmov=-FIRESTEP;
                    pl->ymov=0;
                    pl->xcnt=40;
                        pl->ycnt=0;
                    SPRITEAnim(0,ALEFT,1);
                        break;
                    case FACERIGHT:
                        GPlayerRunLine=((pl->y >> 5)-4)+4;
                    pl->xmov=FIRESTEP;
                    pl->ymov=0;
                    pl->xcnt=40;
                        pl->ycnt=0;
                    SPRITEAnim(0,ARIGHT,1);
                    break;
                    }
                    Moved=TRUE;
                    FireWait=8;
            }
            else if(FireWait>0) FireWait--;

            if(!Moved) {
            switch(GPlayerFacing) {
                    case FACEUP:
                        SPRITEAnim(0,AUP,1);
                        break;
                    case FACEDOWN:
                        SPRITEAnim(0,ADOWN,1);
                        break;
                    case FACELEFT:
                        SPRITEAnim(0,ALEFT,1);
                        break;
                    case FACERIGHT:
                        SPRITEAnim(0,ARIGHT,1);
                        break;
                    }
            }
            break;

        case RUNTO:
            cl=GPlayerCollision();

            if(cl!=0 || pl->x==0 ||pl->y==416 ||pl->x==608 ||pl->y==0) {
            pl->xmov=0;
            pl->ymov=0;
            pl->xcnt=0;
            pl->ycnt=0;
            if(BombTwoFired==TRUE) {
                    pl->id=LastBombKill;
                    if(pl->id<=COLOR4) pl->id+=PCOLOR1;
                }

            Accum=1;
            GPlayerMode=RUNFROM;
            switch(GPlayerFacing) {
                    case FACEUP:
                        GPlayerFacing=FACEDOWN;
                    SPRITEAnim(0,ADOWN,1);
                        break;
                    case FACEDOWN:
                        GPlayerFacing=FACEUP;
                    SPRITEAnim(0,AUP,1);
                        break;
                    case FACELEFT:
                        GPlayerFacing=FACERIGHT;
                    SPRITEAnim(0,ARIGHT,1);
                        break;
                    case FACERIGHT:
                        GPlayerFacing=FACELEFT;
                    SPRITEAnim(0,ALEFT,1);
                        break;
                    }
            }
            break;

        case RUNFROM:
            Accum=1; LastKill=-1;
            Bomb2Sound=FALSE;
            switch(GPlayerFacing) {
            case FACEDOWN:
                    pl->xmov=0;
                    pl->ymov=FIRESTEP;
                    pl->xcnt=0;
                    pl->ycnt=40;
                    break;
            case FACEUP:
                    pl->xmov=0;
                    pl->ymov=-FIRESTEP;
                    pl->xcnt=0;
                    pl->ycnt=40;
                    break;
            case FACERIGHT:
                    pl->xmov=FIRESTEP;
                    pl->ymov=0;
                    pl->xcnt=40;
                    pl->ycnt=0;
                    break;
            case FACELEFT:
                    pl->xmov=-FIRESTEP;
                    pl->ymov=0;
                    pl->xcnt=40;
                    pl->ycnt=0;
                    break;
            }
            if(GPlayerFacing==FACELEFT && pl->x >= GStartRunx) {
            if(pl->x-16 < GStartRunx) {
                    pl->xmov=0;
                    pl->ymov=0;
                    pl->xcnt=0;
                    pl->ycnt=0;
                    pl->x=GStartRunx;
                    GPlayerMode=INMIDDLE;
                    GPlayerRunLine=-1;
                    }
            }
            if(GPlayerFacing==FACERIGHT && pl->x <= GStartRunx) {
            if(pl->x+16 > GStartRunx) {
                    pl->xmov=0;
                    pl->ymov=0;
                    pl->xcnt=0;
                    pl->ycnt=0;
                    pl->x=GStartRunx;
                    GPlayerMode=INMIDDLE;
                    GPlayerRunLine=-1;
                    }
            }
            if(GPlayerFacing==FACEUP && pl->y >= GStartRuny) {
            if(pl->y-16 < GStartRuny) {
                    pl->xmov=0;
                    pl->ymov=0;
                    pl->xcnt=0;
                    pl->ycnt=0;
                    pl->y=GStartRuny;
                    GPlayerMode=INMIDDLE;
                    GPlayerRunLine=-1;
                    }
            }
            if(GPlayerFacing==FACEDOWN && pl->y <= GStartRuny) {
            if(pl->y+16 > GStartRuny) {
                    pl->xmov=0;
                    pl->ymov=0;
                    pl->xcnt=0;
                    pl->ycnt=0;
                    pl->y=GStartRuny;
                    GPlayerMode=INMIDDLE;
                    GPlayerRunLine=-1;
                    }
            }
            break;
        }

    //Light Beam

    if(((GSprite[0].x & 31)==0) && ((GSprite[0].y & 31)==0) && GPlayerMode!=INMIDDLE) {
        if(DetailLevel==0) {
            BeamSpr=SPRITEGetFree();
            Bm=&GSprite[BeamSpr];
            Bm->id=LIGHTBEAM;
            Bm->line=-1;
            Bm->walk=FALSE;
            Bm->fly=FALSE;
            Bm->oldx=0;
            Bm->oldy=0;
            Bm->xmov=0;
            Bm->ymov=0;
            Bm->clag=65000;
            Bm->x=GSprite[0].x;
            Bm->y=GSprite[0].y;
            Bm->active=TRUE;

            UWORD PID=pl->id;
            UWORD beam;
            if(PID>=PCOLOR1 && PID<=PCOLOR4) PID-=PCOLOR1;
            if(GSprite[0].id>=BOMB1 && GSprite[0].id<=BOMB3) beam=4*4;
            else beam=4*PID;
            switch(GPlayerFacing) {
            case FACEUP:
                    SPRITENewAnim(BeamSpr,beam+2,2);
                    break;
            case FACEDOWN:
                    SPRITENewAnim(BeamSpr,beam+3,2);
                    break;
            case FACELEFT:
                    SPRITENewAnim(BeamSpr,beam+1,2);
                    break;
            case FACERIGHT:
                    SPRITENewAnim(BeamSpr,beam,2);
                    break;
            }
            }
        }
}

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

void WinPlayer()
{
    UWORD i;
    UWORD exc,exx,cl;
    WORD bx,by;
    UDWORD Turn;
    SPRITE *s;

    s=&GSprite[0];

    FireEnableStatus=FALSE;

    for(i=0;i<80;i++) {
    FindLineCount();
    MonitorWalking();
        if(GPlayerMode!=INMIDDLE) {
        GControlPlayer();
        }
        else {
        break;
        }
        PANELUpdate();
        GRAPHICUpdate();
        }
}

// Player explosion

void KillPlayer()
{
    UWORD i;
    UWORD exc,exx,cl;
    WORD bx,by;
    UDWORD Turn;
    SPRITE *s;

    s=&GSprite[0];

    FireEnableStatus=FALSE;

    for(i=0;i<80;i++) {
    FindLineCount();
    MonitorWalking();
        if(GPlayerMode!=INMIDDLE) {
        GControlPlayer();
        }
        else {
        break;
        }
        PANELUpdate();
        GRAPHICUpdate();
        }

   s->animtog=FALSE;

    s->fly=TRUE;
    s->dline=SPRLINE;
    s->sline=1;
    s->pline=5;
    s->clag=65000;
    s->update=TRUE;
    WorkoutLine(s,s->x,s->y,304,208);

    while(s->dline!=NOLINE) {
        PANELUpdate();
        GRAPHICUpdate();
        }

    SoundPlay(10);

    Turn=GSprite[0].anim << 16;

    for(exc=0;exc<14;exc++) {
        if(exc<13) {
            cl=SPRITEGetFree();
            GSprite[cl].active=TRUE;
            GSprite[cl].id=EXPLODE;
            GSprite[cl].walk=FALSE;
            GSprite[cl].fly=TRUE;
            GSprite[cl].line=-1;
            GSprite[cl].xmov=0;
            GSprite[cl].ymov=0;
            GSprite[cl].oldx=0;
            GSprite[cl].oldy=0;
            GSprite[cl].dline=NOLINE;
            GSprite[cl].clag=65000;

            bx=rand() & 31;
            by=rand() & 31;

            GSprite[cl].x=(GSprite[0].x-16)+bx;
            GSprite[cl].y=(GSprite[0].y-16)+by;

            SPRITENewAnim(cl,1,0);
            GSprite[cl].animtime=3;
            }

        for(exx=0;exx<8;exx++) {
            if((NEWspr!=-1) || (WalkLine!=-1)) {
            NewEnemy();
            EnemyAppear();
            MonitorWalking();
                }
            Turn+=16384;
            if((Turn>>16) > 4) Turn=0;
            SPRITENewAnim(0,Turn>>16,0);    //spin player sprite

            PANELUpdate();
        GRAPHICUpdate();
            }
        SoundPlay(10);

        if(exc==13) {
            //304 208
            SetStar(272,208,272-32,208);    //left side
            SetStar(336,208,336+32,208);    //right side

            SetStar(272+8,176+8,(272-32)+12,(176-32)+12); //top left
            SetStar(272+8,240-8,(272-32)+12,(240+32)-12); //bottom left

            SetStar(304,176,304,176-32);    //top mid
            SetStar(304,240,304,240+32);    //bottom mid

            SetStar(336-8,176+8,(336+32)-12,(176-32)+12); //top right
            SetStar(336-8,240-8,(336+32)-12,(240+32)-12); //bottom right
            }
        }

    for(exx=0;exx<4;exx++) {
        PANELUpdate();
        GRAPHICUpdate();
        }

    GSprite[0].id=EXPLODE;
    SPRITENewAnim(0,1,0);

    for(exx=0;exx<8;exx++) {
        PANELUpdate();
        GRAPHICUpdate();
        }
    SoundPlay(10);

    for(exx=0;exx<64;exx++) {
        PANELUpdate();
        GRAPHICUpdate();
        }

    // Switch off all animations
    for( i=0; i < NUMSPRITES; i++) GSprite[i].animtog = FALSE;

    for(exx=0;exx<4;exx++) {
        PANELUpdate();
        GRAPHICUpdate();
        }
    FireEnableStatus=TRUE;
}

//===========================================================================
// Score functions
//===========================================================================

// Fill string with leading zeros

void PadLeadingZeros(BYTE *s,UWORD l)
{
    UWORD pos,len;
    BYTE tmpa[32],tmpb[32];
    BYTE *p;

    strcpy(tmpa,"0000000000");
    strcat(tmpa,s);

    len=strlen(tmpa);
    pos=len-l;

    p=&tmpb[0];
    while(pos!=len) {
        *(p++)=tmpa[pos];
        pos++;
       }
    tmpb[pos]=0;
    strcpy(s,tmpb);
}

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

// Maintain the rolling score digits

void ScoreTrack()
{
    WORD i;

    for(i=0;i<7;i++) {
        if(GSprite[Score[i]].animtog==TRUE) return;
        }

    BOOL spin=FALSE;
    for(i=0;i<7;i++) {
        if(GSprite[Score[i]].anim==1) {
        GSprite[Score[i]].id=DIGIT0+Digit[i];
        SPRITENewAnim(Score[i],2,0);
        spin=TRUE;
        }
        }
    if(spin==TRUE) return;

    if(GBScore==0 || GPlayerMode==RUNTO) return;

    GScore+=GBScore;
    GBScore=0;

    BYTE tmp[20];
    itoa(GScore,tmp,10);
    i=7-strlen(tmp);

    UWORD dp=0,aw=1;
    BOOL RollOn=FALSE;

    while(i<7) {
        Digit[i]=tmp[dp++]-48;
        if(Digit[i]!=ODigit[i] || RollOn==TRUE) {
        GSprite[Score[i]].id=DIGIT0+ODigit[i];
        SPRITENewAnim(Score[i],1,0);
        ODigit[i]=Digit[i];
        RollOn=TRUE;
        }
        i++;
        }
    SPRITENewAnim(Score[5],1,0);
    SPRITENewAnim(Score[6],1,0);
}

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

// Maintain the number of kills digits

void KillsTrack()
{
    WORD i;

    for(i=0;i<4;i++) {
        if(GSprite[SKill[i]].animtog==TRUE) return;
        }

    BOOL spin=FALSE;
    for(i=0;i<4;i++) {
        if(GSprite[SKill[i]].anim==1) {
        GSprite[SKill[i]].id=DIGIT0+KDigit[i];
        SPRITENewAnim(SKill[i],2,0);
        spin=TRUE;
        }
        }
    if(spin==TRUE) return;

    if(GBKills==0 || GPlayerMode==RUNTO) return;

    GBKills=0;

    BYTE tmp[20];
    if(Kills<0) Kills=0;
    itoa(Kills,tmp,10); //Kills counting down

    PadLeadingZeros(tmp,4);
    i=0;

    UWORD dp=0;
    BOOL RollOn=FALSE;

    while(i<4) {
        KDigit[i]=tmp[dp++]-48;
        if((KDigit[i]!=KODigit[i]) || RollOn==TRUE) {
        GSprite[SKill[i]].id=DIGIT0+KODigit[i];
        SPRITENewAnim(SKill[i],1,0);
        KODigit[i]=KDigit[i];
            RollOn=TRUE;
        }
        i++;
        }
}

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

// Maintain the level number digits

void LevelsTrack()
{
    WORD i;

    for(i=0;i<2;i++) {
        if(GSprite[SLevel[i]].animtog==TRUE) return;
        }

    BOOL spin=FALSE;
    for(i=0;i<2;i++) {
        if(GSprite[SLevel[i]].anim==1) {
        GSprite[SLevel[i]].id=DIGIT0+LDigit[i];
        SPRITENewAnim(SLevel[i],2,0);
        spin=TRUE;
        }
        }
    if(spin==TRUE) return;

    if(Level==OldLevel) return;
    OldLevel=Level;

    BYTE tmp[20];
    itoa(Level,tmp,10);
    i=2-strlen(tmp);

    UWORD dp=0;

    while(i<2) {
        LDigit[i]=tmp[dp++]-48;
        if(LDigit[i]!=LODigit[i]) {
        GSprite[SLevel[i]].id=DIGIT0+LODigit[i];
        SPRITENewAnim(SLevel[i],1,0);
        LODigit[i]=LDigit[i];
        }
        i++;
        }
}

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

// Setup the score digit sprites

void ScoreSetup()
{
    UWORD fs;
    BYTE tmp[20];

    for(UWORD i=0;i<7;i++) {
        fs=SPRITEGetFree();
        Digit[i]=0;
        ODigit[i]=0;
        Score[i]=fs;
        GSprite[fs].active=TRUE;
        GSprite[fs].y=32;
        GSprite[fs].x=i*32;
        GSprite[fs].id=DIGIT0;
        GSprite[fs].walk=FALSE;
        GSprite[fs].fly=FALSE;
        GSprite[fs].spawnid=-1;
        SPRITENewAnim(fs,0,0);
        }

    itoa(Kills,tmp,10);
    PadLeadingZeros(tmp,4);

    for(i=0;i<4;i++) {
        fs=SPRITEGetFree();
        KDigit[i]=tmp[i]-48;
        KODigit[i]=tmp[i]-48;
        SKill[i]=fs;
        GSprite[fs].active=TRUE;
        GSprite[fs].y=32;
        GSprite[fs].x=(32*16)+(i*32);
        GSprite[fs].id=DIGIT0+tmp[i]-48;
        GSprite[fs].walk=FALSE;
        GSprite[fs].fly=FALSE;
        GSprite[fs].spawnid=-1;
        SPRITENewAnim(fs,0,0);
        }

    for(i=0;i<2;i++) {
        fs=SPRITEGetFree();
        LDigit[i]=0;
        LODigit[i]=0;
        SLevel[i]=fs;
        GSprite[fs].active=TRUE;
        GSprite[fs].y=416-32;
        GSprite[fs].x=(32*16)+(2*32)+(i*32);
        GSprite[fs].id=DIGIT0+i;
        GSprite[fs].walk=FALSE;
        GSprite[fs].fly=0;
        GSprite[fs].spawnid=-1;
        SPRITENewAnim(fs,0,0);
        }
    LODigit[1]=1;
}

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

void NetSpriteSetup()
{
    DWORD i, p;
    UWORD fs;

    p = 0;

    for(i=0;i<20;i++) {
        fs = SPRITEGetFree();
        NET_Name_Sprite[ i ] = fs;
        GSprite[fs].active=TRUE;
        GSprite[fs].y=448;
        GSprite[fs].x=48 + p;
        NET_Name_Posx[ i ] = 48 + p;
        GSprite[fs].id=NETFONT;
        GSprite[fs].walk=FALSE;
        GSprite[fs].fly=FALSE;
        GSprite[fs].spawnid=-1;
        SPRITENewAnim(fs,47,0);
        NET_Name_Letter[ i ] = ' ';
        if( ( (i+1) % 5 ) == 0 ) p += 64;
        p += 16;
        }
}

//===========================================================================
// Setup for player
//===========================================================================

void PlayerSetup()
{
    GSprite[0].active=TRUE;
    GSprite[0].x=MAPUpdate[(10*MAPWIDTH)+20].x;
    GSprite[0].y=MAPUpdate[(10*MAPWIDTH)+20].y;
    GSprite[0].id=PCOLOR1+(rand() & 3);
    GSprite[0].walk=FALSE;
    GSprite[0].fly=FALSE;
    GSprite[0].spawnid=-1;
    GPlayerMode=INMIDDLE;
    GPlayerRunLine=-1;
    SPRITENewAnim(0,2,1);
}

//===========================================================================
// Set real state variables for level
//===========================================================================

void SetLevel(UWORD newlevel)
{
    UWORD lev = newlevel;
    BYTE tmp[32];

    lev--;

    if( lev > 9 ) lev = 9;

    NET_Player_Level = lev;

    TimeSpeed=LevelSet[lev].Speed;
    LevelBombs=LevelSet[lev].Bombs;

    Kills=LevelSet[lev].Kills;

    GColorThreshold=LevelSet[lev].ColorThreshold;
    WhichBombs=LevelSet[lev].WhichBombs;
    BonusRand=LevelSet[lev].BonusRand;

    if( newlevel < MAXLEVELS+1 ) {
        strcpy( tmp, "gfx\\" );
        strcat( tmp, LevelSet[lev].BackImage );
        BackPcx.Load( tmp, buf3 );
        }

    EnemyLeadIn=DifficultySet[Difficulty-1];

    StrobeColor1=LevelSet[lev].StrobeColor1;
    StrobeColor2=LevelSet[lev].StrobeColor2;
    StrobeR1=BackPcx.palette[StrobeColor1*3];
    StrobeG1=BackPcx.palette[(StrobeColor1*3)+1];
    StrobeB1=BackPcx.palette[(StrobeColor1*3)+2];
    StrobeR2=BackPcx.palette[StrobeColor2*3];
    StrobeG2=BackPcx.palette[(StrobeColor2*3)+1];
    StrobeB2=BackPcx.palette[(StrobeColor2*3)+2];

    switch(lev) {
        case 0:
            MusicPlay(29);
            break;
        case 1:
            MusicPlay(30);
            break;
        case 2:
            MusicPlay(31);
            break;
        case 3:
            MusicPlay(32);
            break;
        case 4:
            MusicPlay(33);
            break;
        case 5:
            MusicPlay(34);
            break;
        case 6:
            MusicPlay(29);
            break;
        case 7:
            MusicPlay(30);
            break;
        default:
            MusicPlay(29);
            break;
        }
}

//===========================================================================
// Create preset number of enemies
//===========================================================================

void CreateEnemyLeadIn()
{
    UWORD i;

    if(EnemyLeadIn == 0) return;

    DoingLeadIn=TRUE;
    for(i=0;i<EnemyLeadIn;i++) {
      FindLineCount();
        NEWspr=-1;
        CreateEnemy();
        AppearCount=0;
      NewInstantEnemy();
        EnemyAppear();
        GRAPHICUpdateNotVisible();
        }

    GMorphPal(128,192,8,(UBYTE *) WPalette);  //Strobe while flash
    GMorphPal(0,255,8,(UBYTE *) BackPcx.palette);

    GRAPHICUpdate();
    DoingLeadIn=FALSE;
}

//===========================================================================
// Next level functions
//===========================================================================

void VertCopy(WORD x,WORD y,WORD w,WORD h,WORD bottom)
{
   TUPDATE *m1,*m2;
    UDWORD pos1,pos2,i;
    UDWORD tstart,tend,bstart,bend;

    if(y<0) y=0;
    if(h>bottom) h=bottom;
    //if(h>479) h=479;
    if(x<0) x=0;
    if(w>639) w=639;

    // Copy the bitmap

    pos1=Yoffset[y] + x;
    pos2=Yoffset[y] + x+w;
    for(i=0;i<h+1;i++) {
        *(buf1+pos1)=*(buf2+pos1);
        *(buf1+pos2)=*(buf2+pos2);
        pos1+=640; pos2+=640;
        }

    // Now update map positions

    tstart=y>>4;
    tend=(y+h)>>4;
    bstart=Moffset[y>>4]+((x+w)>>4);
   bend=Moffset[(y+h)>>4]+((x+w)>>4);
   m1=&MAPUpdate[Moffset[tstart]+(x>>4)];
   m2=&MAPUpdate[bstart];
    for(i=tstart;i<=tend;i++) {
        m1->phase=1;
        m2->phase=1;
        m1+=MAPWIDTH;
        m2+=MAPWIDTH;
        }
}

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

inline void MarkCopy(WORD x,WORD y)
{
    UDWORD pos1;
    UDWORD tstart;

    if(y<0) y=0;
    //if(y>447) y=447;
    if(y>479) y=479;
    if(x<0) x=0;
    if(x>639) x=639;

    // Copy the bitmap

    pos1=Yoffset[y] + x;
    *(buf1+pos1)=*(buf2+pos1);

    // Now update map positions

    tstart=Moffset[y>>4]+(x>>4);
   MAPUpdate[tstart].phase=1;
}

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

void HorzCopy(WORD x,WORD y,WORD w,WORD h)
{
   TUPDATE *m1,*m2;
    UDWORD pos1,pos2,i;
    UDWORD tstart,tend,bstart,bend;

    if(x<0) x=0;
    if(w>639) w=639;
    if(y<0) y=0;
    //if(h>447) h=447;
    if(h>479) h=479;

    // Copy the bitmap rectangle

    pos1=Yoffset[y] + x;
    memcpy(buf1+pos1,buf2+pos1,w);
    pos1=Yoffset[y+h] + x;
    memcpy(buf1+pos1,buf2+pos1,w);

    // Now update map positions

    tstart=Moffset[y>>4]+(x>>4);
    tend=Moffset[y>>4]+((x+w)>>4);
    bstart=Moffset[(y+h)>>4]+(x>>4);
   bend=Moffset[(y+h)>>4]+((x+w)>>4);
   m1=&MAPUpdate[tstart];
   m2=&MAPUpdate[bstart];
    for(i=tstart;i<=tend;i++) {
        m1->phase=1;
        m2->phase=1;
        m1++;
        m2++;
        }
}

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

void THorzCopy(WORD x,WORD y,WORD w)
{
   TUPDATE *m1;
    UDWORD pos1,i;
    UDWORD tstart,tend;

    if(x<0) x=0;
    if(x>639) x=639;
    if(w+x>639) w=640-x;
    //if(y>447) y=447;
    if(y>479) y=479;
    if(y<0) y=0;

    // Copy the bitmap rectangle

    pos1=Yoffset[y] + x;
    memcpy(buf1+pos1,buf2+pos1,w);

    // Now update map positions

    tstart=Moffset[y>>4]+(x>>4);
    tend=Moffset[y>>4]+((x+w)>>4);

    m1=&MAPUpdate[tstart];

    i=tstart;
    while( (i<=tend) && (i<MAPSIZE)) {
        m1->phase=1;
        m1++;
        i++;
        }
}

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

void RectGrow()
{
    UDWORD i,j,k;
   DWORD x,y;
    DWORD w,h;
    DWORD v;

    memcpy(buf2,buf3,307200);
    GSprite[0].forcedupdate=TRUE;
    SPRITEPrepare();
    SPRITEUpdateBuffer2();

    v=1<<16;
    x=319-64; y=223-64;
    w=129; h=129;

    while(x>0) {
        WaitTimer();
        NET_Update();
        MAPRefresh();

        for(i=0;i<(v>>16);i++) {
            if(y>0) {
                for(j=0;j<3;j++) {
                    HorzCopy(x-j,y-j,w+(j*2),h+(j*2));
                    }
                }
            if(x>0) {
                for(j=0;j<4;j++) {
                    VertCopy(x-j,y-j,w+(j*2),h+(j*2),447);
                    }
                }
            x-=4; w+=8;
            y-=3; h+=6;

            if(x<0 && y<0) break;
            }
        v+=4096;
        BACKMapRefresh();
        }

    MAPRefresh();

    for( i=0; i<33; i++ ) {
        HorzCopy(0,0,640,447+i);
        }

    BACKMapRefresh();

    v=1<<16;
    x=319; y=223;
    w=1; h=1;

    while(x>319-64) {
        WaitTimer();
        MAPRefresh();

        for(i=0;i<(v>>16);i++) {
            if(y>0) {
                for(j=0;j<4;j++) {
                    HorzCopy(x-j,y-j,w+(j*2),h+(j*2));
                    }
                }
            if(x>0) {
                for(j=0;j<4;j++) {
                    VertCopy(x-j,y-j,w+(j*2),h+(j*2),446);
                    }
                }
            x-=4; w+=8;
            y-=4; h+=8;

            if(x<0) break;
            }
        v+=4096;
        BACKMapRefresh();
        }
    BACKMapRefresh();
    MAPRefresh();

    memcpy(buf2,buf3,307200);

    GRAPHICUpdate();
   GRAPHICUpdate();
}

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

void RectGrowFullScreen()
{
    UDWORD i,j,k;
   DWORD x,y;
    DWORD w,h;
    DWORD v;

    memcpy(buf2,buf3,307200);

    v=1<<16;
    x=319; y=239;
    w=1; h=1;

    while(x>0) {
        WaitTimer();
        NET_Update();
        MAPRefresh();

        for(i=0;i<(v>>16);i++) {
            if(y>0) {
                for(j=0;j<4;j++) {
                    HorzCopy(x-j,y-j,w+(j*2),h+(j*2));
                    }
                }
            if(x>0) {
                for(j=0;j<4;j++) {
                    VertCopy(x-j,y-j,w+(j*2),h+(j*2),479);
                    }
                }
            x-=4; w+=8;
            y-=4; h+=8;

            if(x<0) break;
            }
        v+=4096;
        BACKMapRefresh();
        }
    BACKMapRefresh();
    MAPRefresh();

    memcpy(buf2,buf3,307200);

    GRAPHICUpdate();
   GRAPHICUpdate();
}

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

void ShuffleOff()
{
    DWORD i,j,k;
    DWORD LineCount,CurrLine;
    WORD Spr,mx,my,cx,cy,Cline;
    BOOL DoneSound;
    SPRITE *s;

    for(CurrLine=0;CurrLine<16;CurrLine++) {
        DoneSound=FALSE;

        Cline=CurrLine;

        if(Cline<4) {
            mx=0; my=-16;
            cx=0; cy=2;
            }
        if(Cline>3 && Cline<8) {
            mx=16; my=0;
            cx=2; cy=0;
            }
        if(Cline>7 && Cline<12) {
            mx=0; my=16;
            cx=0; cy=2;
            Cline=11-(Cline-8);
            }
        if(Cline>11) {
            mx=-16; my=0;
            cx=2; cy=0;
            Cline=15-(Cline-12);
            }

        FindLineCount();

        while(GLine[Cline].count > 0) {
            Spr=LastOnLine(Cline+1);
            if(Spr!=0) {
                s=&GSprite[Spr];
                s->id=EXPLODE;
            s->walk=FALSE;
            s->oldx=0;
            s->oldy=0;
            s->xmov=0;
            s->ymov=0;
                s->line=-1;
            SPRITENewAnim(Spr,0,0);
            s->animtime=2;
                }

            for(i=1;i<NUMSPRITES;i++) {
                s=&GSprite[i];
                if(s->active==TRUE && ((s->id>=COLOR1 && s->id<=COLOR4) || (s->id>=BOMB1 && s->id<=BOMB3) ||
                (s->id==BONUSB) ) && s->line==Cline+1) {
                    s->xmov=mx; s->ymov=my;
                    s->xcnt=cx; s->ycnt=cy;
                    }
                }
            if(!DoneSound) {
                //SoundPlay(10);
                DoneSound=TRUE;
                }

            for(j=0;j<2;j++) {
                PANELUpdate();
                GRAPHICUpdate();
            FindLineCount();
                }
            }
        }

    for(j=0;j<24;j++) {
        PANELUpdate();
        GRAPHICUpdate();
        }

    //Tidy up any trivial sprites
    s=&GSprite[1];
    for(i=1;i<NUMSPRITES;i++) {
        if(s->active==TRUE && ((s->id>=COLOR1 && s->id<=COLOR4) || (s->id>=BOMB1 && s->id<=BOMB3) ||
            (s->id==BONUSB) )) {
        s->active=FALSE;
        }
        else {
        s->update=TRUE;
        s->forcedupdate=TRUE;
        s->oldx=0;
        s->oldy=0;
        }
        s++;
        }
}

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

void NextLevelSequence()
{
    SPRITE *d1,*d2,*s;
    UWORD i;
    BOOL DoKill,DoFly;

    if( GPlayerMode != INMIDDLE ) return;
    if( LineState == BEING_BUILT ) return;

    FireEnableStatus=FALSE;

    for( i=0; i<32; i++ ) {
      GControlPlayer();
    MonitorWalking();
        PANELUpdate();
        GRAPHICUpdate();
        }

    if( Level >= MAXLEVELS && GameMode == CONTINUE ) {
        if( Level < 99 ) Level++;
        SetLevel(Level);
        GBKills=999;  //Make sure kills is updated (dummy value)
        FireEnableStatus=TRUE;
        return;
        }

    // Wait for beam to terminate
    DoKill=TRUE;
    while(DoKill==TRUE) {
      GControlPlayer();
        PANELUpdate();
        GRAPHICUpdate();
        DoKill=FALSE;
        for(i=1;i<NUMSPRITES;i++) {
        if((GSprite[i].active==TRUE) && (GSprite[i].id==LIGHTBEAM)) DoKill=TRUE;
        }
        }

    // Wait for flying sprites to finish
    DoFly=TRUE;
    while(DoFly==TRUE) {
      GControlPlayer();
        PANELUpdate();
        GRAPHICUpdate();
        DoFly=FALSE;
        for(i=1;i<NUMSPRITES;i++) {
        if((GSprite[i].active==TRUE) && (GSprite[i].dline!=NOLINE)) DoFly=TRUE;
        }
        }

    // Wait till player stops moving
    while( ((GSprite[0].x & 31) != 0) || ((GSprite[0].y & 31) != 0) ) {
        PANELUpdate();
    GRAPHICUpdate();
        }
    PANELUpdate();
   GRAPHICUpdate();

    //--New code: Remove strobe 5/9 jj-------------------------------------------
    if( NEWspr != -1 )
    {
        s = &GSprite[ NEWspr ];

        s->id=0;
        s->walk=FALSE;
        s->fly=FALSE;
        s->line=-1;
        s->xmov=0;
        s->ymov=0;
        s->oldx=0;
        s->oldy=0;
        s->clag=0;
        s->spawnid=-1;
        s->active=FALSE;
        NEWspr=-1;
        LineState = BUILT;

        AppearCount = 0;
    }
    //---------------------------------------------------------------------------

    SoundPlay(1);

    if( Level < 99 ) Level++;
    SetLevel(Level);

    if(GameMode==LEVELPLAY) {
        ShuffleOff();
        LineState = BEING_BUILT;
        }
    else {
        s=&GSprite[1];
        for(i=1;i<NUMSPRITES;i++) {
            // None Destructive next level
            if(s->active==TRUE && ((s->id>=EXPLODE && s->id<=LINEBONUS)) && (s->id != STROBE) ) {
            s->active=FALSE;
            }
            else {
            s->update=TRUE;
            s->forcedupdate=TRUE;
            s->oldx=0;
            s->oldy=0;
            }
            s++;
            }
        }

    FindLineCount();

    PANELUpdate();
   GRAPHICUpdate();

   StrobeVal1=0;
   SPal(StrobeColor1,StrobeR1,StrobeG1,StrobeB1);
   StrobeVal2=0;
   SPal(StrobeColor2,StrobeR1,StrobeG2,StrobeB2);
    StrobeSound=0;

    if( NET_Game == 0 ) {
        BackPcx.palette[ NET_Back_Color * 3 ] = 0;
        BackPcx.palette[ NET_Back_Color * 3 + 1 ] = 0;
        BackPcx.palette[ NET_Back_Color * 3 + 2 ] = 0;
        }
    SetPal((UBYTE *) BackPcx.palette);

    if(Level < 11) RectGrow();

    GSprite[0].forcedupdate=TRUE;

    GBKills=999;  //Make sure kills is updated (dummy value)

    if(GameMode==LEVELPLAY) CreateEnemyLeadIn();

    FireEnableStatus=TRUE;
}

//===========================================================================
// Network message handling
//===========================================================================

void Net_Check_Message()
{
    DWORD receive;

    if( GameMode != MULTI ) return;

    if( ! NETCheckEvent() ) return;

    receive = NETFindPlayerFrom();

    switch( NET_Player_Event ) {
        case 'A':
            BlockAddAmount = NET_Player_Value;
            BlockAddColor = ( rand() & 3 );
            EVENT_AddBlock++;
            if( receive != -1 ) NETReceiveArrows( receive );
            break;
        case 'B':
            EVENT_BlowUp++;
            if( receive != -1 ) NETReceiveArrows( receive );
            break;
        case 'C':
            SpeedUpDuration = 100;
            EVENT_SpeedUp++;
            if( receive != -1 ) NETReceiveArrows( receive );
            break;
        case 'D':
            EVENT_AddTarget++;
            break;
        }
}

//===========================================================================
// Pre-Game setup function
//===========================================================================

void PrepareToPlay()
{
    DWORD i;

    BlackPal();
    Svga.Cls();
    GSetup();
    PlayerSetup();
    ScoreSetup();
    SpringSetup();

    if( NET_Game == 1 ) NetSpriteSetup();

    Svga.Cls();
    memset(buf1,0,307200);
    memset(buf2,0,307200);

    for(i=0;i<NUMSPRITES;i++) {
        if(GSprite[i].active==TRUE) {
            GSprite[i].forcedupdate=TRUE;
            }
        }

    if( NET_Game == 1 ) {
        NETSetPlayerLevelSprings();
        EVENT_AddTarget = NET_Player_Springs;
        Level = NET_Player_Level+1;
        EnemyLeadIn = NET_Player_Blocks;
        }

    if( NET_Game == 0 ) {
        BackPcx.palette[ NET_Back_Color * 3 ] = 0;
        BackPcx.palette[ NET_Back_Color * 3 + 1 ] = 0;
        BackPcx.palette[ NET_Back_Color * 3 + 2 ] = 0;
        }

    SetPal((UBYTE *) BackPcx.palette);
    RectGrow();
    CreateEnemyLeadIn();
}

//===========================================================================
// Configuration file functions
//===========================================================================

void SetAdjustables()
{
    PlayerSpeed = 4;
    PlayerMoveBy = 32 / PlayerSpeed;
    DetailLevel = 0;
}

//===========================================================================
// Game over circle transition
//===========================================================================

void CreateCircleTable (DWORD xc, DWORD yc, DWORD r, WORD * table)
{
   WORD * buffer = table;

   for (DWORD i = 448; i > 0; i --)
      * buffer ++ = 320;

   DWORD x = 0,
       y = r;

   DWORD k = r * r;

   DWORD t = -2 * r * k + k,
       t1 = 4 * k - 4 * r * k,
       t2 = 2 * k;

   DWORD i1 = yc - y,
       i2 = yc + y,
       i3 = yc - x,
       i4 = yc + x;

   DWORD v1 = xc - x,
       v2 = xc - y;

   for (; x <= y; x ++)
      {
      if (i1 >= 0)
         * (table + i1) = v1;

      if (i2 < 448)
         * (table + i2) = v1;

      if (i3 >= 0)
         * (table + i3) = v2;

      if (i4 < 448)
         * (table + i4) = v2;

      if (t >= 0)
         {
         t += t1;
         y --;
         i1 ++;
         i2 --;
         v2 ++;
         t1 += 4 * r * r;
         }

      t += t2;
      t2 += 4 * r * r;
      i3 --;
      i4 ++;
      v1 --;
      }

   for (i = 448; i > 0; i --)
      {
      buffer --;
      if (* buffer < 0)
         * buffer = 0;
      }
}

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

UDWORD CircA[448*2];

void LoseSequence()
{
    DWORD i;
    WORD * table_lt, * table_rt;

    for( i=0; i < NUMSPRITES; i++) GSprite[i].animtog = FALSE;

    for( i=0; i<64; i++ ) NET_Update();

    if( NET_Game == 0 ) BackPcx.Load("gfx\\endscr.imx",buf3);
    else BackPcx.Load("gfx\\lose.imx",buf3);

    memcpy(buf2,buf3,307200);
   SPRITEResetAll();
    MAPUpdateSetup();

   table_lt = (WORD *) CircA;    // Memory for the table(s)

   table_rt = table_lt + 448;

   for ( i = 0; i < 448; i ++)
      * (table_lt + i) = 0;

   for (DWORD r = 400; r > 0; r -= 8)                     // Radius of growing circle 0 -> 400
      {
        NET_Update();
        CreateCircleTable(320, 224, r, table_rt);          // Determine which bits of circle need plotting

      WaitTimer();
        MAPRefresh();

      WORD * buff2 = table_lt,
            * buff1 = table_rt;

      for (DWORD y = 0; y < 448; y ++)                     // For each line of the display
         {
            DWORD x=*buff2;
            DWORD w=(*buff1) - (*buff2);

            if(w>0 && w<320) THorzCopy(x,y,w);

            x=640 - *buff1;
            w=(640 - *buff2) - x;
            if(w>0 && w<320) THorzCopy(x,y,w);

         buff1 ++;
         buff2 ++;
         }

      WORD * spare = table_lt;
      table_lt = table_rt;
      table_rt = spare;

        BACKMapRefresh();
      }
    Copy2SvgaArea(buf3);

    if( NET_Game == 1 ) {
        while( NETFindGamePlayer() ) {
            WaitTimer();
            NET_Update();
            if(KEY[1]) {
                while(KEY[1]);
                break;
                }
            if(KEY[K_FIRE]) {
                while(KEY[K_FIRE]);
                break;
                }
            if(KEY[K_RETURN]) {
                while(KEY[K_RETURN]);
                break;
                }
            }
        }
    else {
        for(i=0;i<120;i++) {
            WaitTimer();
            if(KEY[1]) {
                while(KEY[1]);
                break;
                }
            if(KEY[K_FIRE]) {
                while(KEY[K_FIRE]);
                break;
                }
            if(KEY[K_RETURN]) {
                while(KEY[K_RETURN]);
                break;
                }
            }
        }
}

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

void WinSequence()
{
    DWORD i;
    WORD * table_lt, * table_rt;

    NET_Player_Wins++;

    for( i=0; i < NUMSPRITES; i++) GSprite[i].animtog = FALSE;

    for( i=0; i<64; i++ ) NET_Update();

    BackPcx.Load("gfx\\win.imx",buf3);
    memcpy(buf2,buf3,307200);
   SPRITEResetAll();
    MAPUpdateSetup();

   table_lt = (WORD *) CircA;    // Memory for the table(s)

   table_rt = table_lt + 448;

   for ( i = 0; i < 448; i ++)
      * (table_lt + i) = 0;

   for (DWORD r = 400; r > 0; r -= 8)                     // Radius of growing circle 0 -> 400
      {
        NET_Update();
      CreateCircleTable(320, 224, r, table_rt);          // Determine which bits of circle need plotting

      WaitTimer();
        MAPRefresh();

      WORD * buff2 = table_lt,
            * buff1 = table_rt;

      for (DWORD y = 0; y < 448; y ++)                     // For each line of the display
         {
            DWORD x=*buff2;
            DWORD w=(*buff1) - (*buff2);

            if(w>0 && w<320) THorzCopy(x,y,w);

            x=640 - *buff1;
            w=(640 - *buff2) - x;
            if(w>0 && w<320) THorzCopy(x,y,w);

         buff1 ++;
         buff2 ++;

         }

      WORD * spare = table_lt;
      table_lt = table_rt;
      table_rt = spare;

        BACKMapRefresh();
      }
    Copy2SvgaArea(buf3);

    for(i=0;i<210;i++) {
        WaitTimer();
        NET_Update();
        if(KEY[1]) {
            while(KEY[1]);
            break;
            }
        if(KEY[K_FIRE]) {
            while(KEY[K_FIRE]);
            break;
            }
        if(KEY[K_RETURN]) {
            while(KEY[K_RETURN]);
            break;
            }
        }
}

//===========================================================================
// I/O Setup
//===========================================================================

void SetupJoystick()
{
    JOYTime();

    for( UDWORD i=0; i<256; i++ ) {
        JOYRead( Joy );
        JoyNormalx = Joy.JOY1x;
        JoyNormaly = Joy.JOY1y;
        }
}

//===========================================================================
// Zoop game sequence
//===========================================================================

extern void ContinueKey();
extern void BootUp();
extern UWORD TitleSequence();
extern UWORD HiScoreSequence();

void Zoop()
{
    BOOL QuitGame, Win, Slow;
    UWORD Option,i,KRepeat;

    QuitGame=FALSE;

    Level=1;

    Option=TitleSequence();

    if(Option==9) QuitGame=TRUE;

    switch( GameMode ) {
        case CONTINUE:
            LevelSet=LevelCon;
            DifficultySet=DifficultyCon;
            NET_Game = 0;
            break;
        case LEVELPLAY:
            LevelSet=LevelLev;
            DifficultySet=DifficultyLev;
            NET_Game = 0;
            break;
        case MULTI:
            LevelSet=LevelNet;
            DifficultySet=0;
            NET_Game = 1;
        }

    MusicEnd();

    KRepeat = 0;

    while(QuitGame==FALSE) {
        UWORD OldScore=-1,OldLevel=-1,OldKills=-1;

        if( NET_Game == 1 ) {
            NETSetPlayerLevelSprings();
            Level = NET_Player_Level+1;
            }

        SetLevel(Level);
        PrepareToPlay();  //Playersetup + fadeon etc

        StartGameClock();

        GGameOver=FALSE;

        Slow = 0;

        while(KEY[1]!=TRUE && GGameOver!=TRUE) {

            NET_Player_Type = NET_GAME_PLAYER;

            for( i=0; i<Slow; i++ ) WaitTimer();

            GRAPHICUpdate();
        FindLineCount();
            PANELUpdate();

            CreateEnemy();
        NewEnemy();
        EnemyAppear();
        MonitorWalking();

            Net_Check_Message();
            Net_Refresh_Names();

            Event_Bonus();
            Event_AddBlock();
            Event_SpeedUp();
            Event_AddTarget();
            Event_BlowUp();

            Event_Pause();

            if( KRepeat == 0 ) {
                //Cheats
                if(KEY[63]==TRUE) Kills=0;
                if(KEY[59]==TRUE) {
                    KRepeat = 10;
                    BlockAddAmount = 5;
                    BlockAddColor = 1;
                    EVENT_AddBlock++;
                    }
                if(KEY[60]==TRUE) {
                    KRepeat = 10;
                    EVENT_SpeedUp++;
                    }
                if(KEY[61]==TRUE) {
                    KRepeat = 10;
                    EVENT_BlowUp++;
                    }
                if(KEY[62]==TRUE) {
                    KRepeat = 10;
                    EVENT_AddTarget++;
                    }
                if(KEY[64]==TRUE) {
                    KRepeat = 10;
                    if( Slow == 0 ) Slow = 2;
                    else Slow = 0;
                    }
                } else KRepeat--;

            if( Kills<1 ) NextLevelSequence();

        GGameOver = GCheckGameOver();
        if( GGameOver == FALSE ) GControlPlayer();
        if( CheckPause() == TRUE ) EVENT_Pause = TRUE; //GamePause();

            Win = Net_Check_Won();
            if( Win ) GGameOver = TRUE;
            }

        FormatTime( GetGameClock(), HiTime );

        NET_Player_Type = NET_LOG_PLAYER;

        if( Win ) WinPlayer();
        else KillPlayer();

        for(i=0;i<20;i++)
            WaitTimer();

        MusicPlay(28);

        if( Win ) WinSequence();
        else LoseSequence();

        SetForFadeOut(0,256);
        MorphPal(0,255,2,APalette);

        if( NET_Game == 0 ) {
            i=HiScoreSequence();
            switch(i) {
                case 1:
                    Level=LastLevel;
                    Difficulty=LastDifficulty;
                    if(GameMode==CONTINUE) LevelSet=LevelCon;
                    else LevelSet=LevelLev;
                    break;
                case 2: // Exit from hiscore - go back to title
                    Level=1;
                    Option=TitleSequence();
                    if(Option==9) QuitGame=TRUE;

                    switch( GameMode ) {
                        case CONTINUE:
                            LevelSet=LevelCon;
                            DifficultySet=DifficultyCon;
                            NET_Game = 0;
                            break;
                        case LEVELPLAY:
                            LevelSet=LevelLev;
                            DifficultySet=DifficultyLev;
                            NET_Game = 0;
                            break;
                        case MULTI:
                            LevelSet=LevelNet;
                            DifficultySet=0;
                            NET_Game = 1;
                        }
                }
            }
        else {
            Option=TitleSequence();

            if(Option==9) QuitGame=TRUE;

            switch( GameMode ) {
                case CONTINUE:
                    LevelSet=LevelCon;
                    DifficultySet=DifficultyCon;
                    NET_Game = 0;
                    break;
                case LEVELPLAY:
                    LevelSet=LevelLev;
                    DifficultySet=DifficultyLev;
                    NET_Game = 0;
                    break;
                case MULTI:
                    LevelSet=LevelNet;
                    DifficultySet=0;
                    NET_Game = 1;
                }
            }
        }
}

//===========================================================================
// Zoop main
//===========================================================================

BOOL CheckUV;
extern void ReadConfig();

void main(int argc, char *argv[])
{
    UWORD i,k;

    MOUSEReset();
    SetupJoystick();

    SoundFx=TRUE;
    SoundMusic=TRUE;
    Difficulty=1;

    // Randomize
    time_t tod;
    time(&tod);
    srand(tod);

    Svga.VesaFirst=TRUE;

    printf("\n\nGeneral MIDI timbres for Yamaha OPL-2 and OPL-3 based sound cards were produced\n");
    printf("by The Fat Man(TM) and developed by K.Weston Phelan and George Alistair Sanger.\n");
    printf("Copyright 1993, The Fat Man(TM)\n");

    FILE *fuv = fopen( "univbe.drv", "r" );
    if( fuv == NULL ) {
        CheckUV = FALSE;
        delay( 60 );
        }
    else {
        CheckUV = TRUE;
        fclose( fuv );
        }

    if( CheckUV ) {
        if( UV_install( "", TRUE ) != TRUE ) {
            printf("UNIVBE Not installed. To run ZOOP(tm) refer to README.TXT\n");
            exit( 1 );
            }
        }

    if(!Svga.Init()) {
    printf("\nVideo Card is not VESA compliant. To run ZOOP(tm) refer to README.TXT\n");
        exit( 1 );
        }

    if(GFXCas.Load("gfx\\zoop.out")==FALSE) {
        printf("\nCannot find file zoop.out\n");
        exit( 1 );
        }

    memset(buf1,0,307200);
    memset(buf2,0,307200);
    memset(buf3,0,307200);

    SetAdjustables();

    if( NETInit() == 0 ) {
        NET_Installed = FALSE;
        }
    else {
        NET_Installed = TRUE;
        printf("\nNetwork card has been found.\n");

        NETSetLookWait( 16 );
        NETSetPulseWait( 32 );
        NETPlayerInit();
        }

    SoundInit();

    TimerSetup();

    SoundLoad("sfx\\sfx00.xmi",0);
    SoundLoad("sfx\\sfx01.xmi",0);
    SoundLoad("sfx\\sfx02.xmi",0);
    SoundLoad("sfx\\sfx03.xmi",0);
    SoundLoad("sfx\\sfx04.xmi",0);
    SoundLoad("sfx\\sfx05.xmi",0);
    SoundLoad("sfx\\sfx06.xmi",0);
    SoundLoad("sfx\\sfx07.xmi",0);
    SoundLoad("sfx\\sfx08.xmi",0);
    SoundLoad("sfx\\sfx09.xmi",0);
    SoundLoad("sfx\\sfx10.xmi",0);
    SoundLoad("sfx\\sfx11.xmi",0);
    SoundLoad("sfx\\sfx12.xmi",0);
    SoundLoad("sfx\\sfx13.xmi",0);
    SoundLoad("sfx\\sfx14.xmi",0);
    SoundLoad("sfx\\sfx15.xmi",0);
    SoundLoad("sfx\\sfx16.xmi",0);
    SoundLoad("sfx\\sfx17.xmi",0);
    SoundLoad("sfx\\sfx18.xmi",0);
    SoundLoad("sfx\\sfx19.xmi",0);
    SoundLoad("sfx\\sfx20.xmi",0);
    SoundLoad("sfx\\sfx21.xmi",0);
    SoundLoad("sfx\\sfx22.xmi",0);
    SoundLoad("sfx\\sfx23.xmi",0);
    SoundLoad("sfx\\sfx24.xmi",0);
    SoundLoad("sfx\\sfx25.xmi",0);
    SoundLoad("sfx\\sfx26.xmi",0);
    SoundLoad("sfx\\sfx27.xmi",0);

    SoundLoad("sfx\\zoopjazz.xmi",0);
    SoundLoad("sfx\\jazz2.xmi",0);
    SoundLoad("sfx\\shake.xmi",0);

    SoundLoad("sfx\\tun1.xmi",0);
    SoundLoad("sfx\\tun2.xmi",0);
   SoundLoad("sfx\\tun3.xmi",0);
    SoundLoad("sfx\\tun4.xmi",0);

    MemAvail();
    printf("\n\nMemory: %s\n",MemFree);

    Svga.GraphOn640x480x256();

    GOBJ=GFXCas.OBJList;

    SetPal((UBYTE *) BackPcx.palette);
    Svga.Cls();

    KEYSetup();
    MAPUpdateSetup();

    ReadConfig();

    MusicPlay(28);

    BootUp();

    Zoop();

    SetForFadeOut(0,256);
    MorphPal(0,255,4,APalette);

    TimerKill();

    KEYReset();

    SoundFree();

    Svga.GraphOff640x480x256();
    Svga.Exit();

    if( NET_Installed ) NETFinish();

    UV_exit();

    exit( 0 );
}