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, ®s, ®s, &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 );
}