summaryrefslogtreecommitdiff
path: root/hardwire/hardwire.c
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2019-06-25 18:37:47 +0000
committerIan C <ianc@noddybox.co.uk>2019-06-25 18:37:47 +0000
commitb2f86e20e052d9923523e29743172ea98266d28f (patch)
tree43399e99e34fcb20188cc4d1dea3644dada13b3b /hardwire/hardwire.c
parentad1c73e9ff2580236887019ec176224f820c4c62 (diff)
Added original Hardwire code.HEADmaster
Diffstat (limited to 'hardwire/hardwire.c')
-rw-r--r--hardwire/hardwire.c2720
1 files changed, 2720 insertions, 0 deletions
diff --git a/hardwire/hardwire.c b/hardwire/hardwire.c
new file mode 100644
index 0000000..f6b5871
--- /dev/null
+++ b/hardwire/hardwire.c
@@ -0,0 +1,2720 @@
+/*
+ Hardwire
+ (c) 1997 Noddybox
+ Written using DJGPP v2 and Allegro 3.0
+
+ Allegro written by Shawn Hargreaves
+
+*/
+static char version[]="1.0";
+
+/* Includes
+*/
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <allegro.h>
+#include "hwpal.h"
+#include "hwdat.h"
+#include "keystr.h"
+
+
+/* Macros
+*/
+#define RND(x) (rand()%(x))
+#define CENTRE(s,y,col) textout_centre(img,font8,s,CX,y,col)
+
+
+/* Types and stuff
+*/
+#define HISC 5
+#define HISCFILE "hwscores"
+
+#define GAME_TYPES 3
+#define TETRIS_WIRE 0
+#define COLUMNS_WIRE 1
+#define TETRIS_CLASSIC 2
+
+#define SPRSIZE 8
+#define MAXPITWIDTH 12
+#define MINPITWIDTH 6
+#define PITDEPTH 17
+#define PITXOFF (SPRSIZE*2+((MAXPITWIDTH-PITWIDTH)*SPRSIZE/2))
+#define PITYOFF SPRSIZE*4
+
+#define PITX(x) (((x)*SPRSIZE)+PITXOFF)
+#define PITY(y) (((y)*SPRSIZE)+PITYOFF)
+
+#define SCOREPX 200
+#define SCOREX (SCOREPX+50)
+#define SCOREY 20
+
+#define LEVELPX 200
+#define LEVELX (LEVELPX+50)
+#define LEVELY 40
+
+#define NEXTPX 200
+#define NEXTX (NEXTPX+50)
+#define NEXTY 100
+
+#define BASECTR (SLOW ? (MODE==TETRIS_CLASSIC ? 30 : 100) : \
+ (MODE==TETRIS_CLASSIC ? 50 : 150))
+
+#define WEIGHTCTR (SLOW ? (MODE==TETRIS_CLASSIC ? 2 : 8) : \
+ (MODE==TETRIS_CLASSIC ? 4 : 12))
+
+
+#define LEVELCTR(x) (MAX((BASECTR-(x)*WEIGHTCTR),3))
+
+#define BOUNCEVAL 10
+
+#define KEYBOUNCE(k,x) if (!key[k]) \
+ x=0; \
+ else \
+ if (x) \
+ x--
+
+#define NODIR -1
+#define LEFT 0
+#define UP 1
+#define RIGHT 2
+#define DOWN 3
+#define PATHLEN (MAXPITWIDTH*PITDEPTH)
+
+#define ROR(x) ((x) ? ((x)-1) : (3))
+#define ROL(x) (((x)+1)%4)
+#define REV(x) (((x)+2)%4)
+
+int xi[4]={-1,0,1,0};
+int yi[4]={0,-1,0,1};
+
+char *modetxt[GAME_TYPES]={"TETRIS WIRE",
+ "COLUMNS WIRE",
+ "TETRIS CLASSIC"};
+
+typedef struct
+ {
+ int data[PITDEPTH][MAXPITWIDTH];
+ int id[PITDEPTH][MAXPITWIDTH];
+ } Pit;
+
+Pit pit;
+
+typedef struct
+ {
+ int x,y;
+ } PathElem;
+
+typedef struct
+ {
+ int len;
+ PathElem p[PATHLEN];
+ } Path;
+
+typedef struct
+ {
+ int score;
+ int level;
+ char name[4];
+ } Hisc;
+
+Hisc hisc[GAME_TYPES][HISC] =
+ {
+ {
+ {500,5,"S.V"},
+ {250,4,"S.V"},
+ {200,3,"S.V"},
+ {150,2,"S.V"},
+ {100,1,"S.V"},
+ },
+ {
+ {500,5,"S.V"},
+ {250,4,"S.V"},
+ {200,3,"S.V"},
+ {150,2,"S.V"},
+ {100,1,"S.V"},
+ },
+ {
+ {500,5,"S.V"},
+ {250,4,"S.V"},
+ {200,3,"S.V"},
+ {150,2,"S.V"},
+ {100,1,"S.V"},
+ },
+ };
+
+/* Key controls
+*/
+#define KQUIT KEY_ESC
+#define KREADME KEY_I
+#define KDEFINE KEY_R
+#define KTOGGLE_MODE KEY_F1
+#define KTOGGLE_LEVEL KEY_F2
+#define KTOGGLE_WIDTH KEY_F3
+#define KTOGGLE_MOTION KEY_F4
+#define KTOGGLE_MUSIC KEY_F5
+#define KBORED KEY_F9
+#define DEF_KLEFT KEY_LEFT
+#define DEF_KRIGHT KEY_RIGHT
+#define DEF_KROTATER KEY_DOWN
+#define DEF_KROTATEL KEY_UP
+#define DEF_KDROP KEY_SPACE
+#define DEF_KPAUSE KEY_P
+
+int KLEFT= KEY_LEFT;
+int KRIGHT= KEY_RIGHT;
+int KROTATER= KEY_DOWN;
+int KROTATEL= KEY_UP;
+int KDROP= KEY_SPACE;
+int KPAUSE= DEF_KPAUSE;
+
+#define GETKEY (keypressed() ? (readkey()>>8) : (-1))
+
+int used_keys[129];
+
+
+/* Object types - defines from less obvious names from DAT file
+*/
+#define PIT_TL SPRITE8_000
+#define PIT_ML SPRITE8_001
+#define PIT_BL SPRITE8_002
+#define PIT_BM SPRITE8_003
+#define PIT_BR SPRITE8_004
+#define PIT_MR SPRITE8_005
+#define PIT_TR SPRITE8_006
+
+#define CLASSIC_WALL SPRITE8_024
+
+#define SPECIAL SPRITE8_007
+#define CROSS SPRITE8_008
+#define UPDOWN SPRITE8_009
+#define ACROSS SPRITE8_010
+#define LEFTDOWN SPRITE8_011
+#define RIGHTDOWN SPRITE8_012
+#define RIGHTUP SPRITE8_013
+#define LEFTUP SPRITE8_014
+#define PATH SPRITE8_015
+
+#define BLOCK1SPR SPRITE8_016
+#define BLOCK2SPR SPRITE8_017
+#define BLOCK3SPR SPRITE8_018
+#define BLOCK4SPR SPRITE8_019
+#define BLOCK5SPR SPRITE8_020
+#define BLOCK6SPR SPRITE8_021
+#define BLOCK7SPR SPRITE8_022
+#define BLOCK8SPR SPRITE8_023
+
+#define HISC_CURS1 SPRITE16_000
+#define HISC_CURS2 SPRITE16_001
+#define HISC_CURS3 SPRITE16_002
+
+#define BLANK (-1)
+
+#define RNDPIECE (RND((RIGHTUP-CROSS))+CROSS)
+
+#define SPR(x) (RLE_SPRITE*)dfile[(x)].dat
+#define SAM(x) (SAMPLE*)dfile[(x)].dat
+
+
+/* Object and game type control types
+*/
+void GenTetrisWirePiece(int special),
+ RotTetrisWirePiece(int dir);
+void GenColumnsWirePiece(int special),
+ RotColumnsWirePiece(int dir);
+void GenTetrisClassicPiece(int special),
+ RotTetrisClassicPiece(int dir);
+void CheckWirePaths(void),
+ CheckClassicPaths(void);
+
+typedef struct
+ {
+ int ox,oy;
+ int data[4][4];
+ } Piece;
+
+typedef struct
+ {
+ int no;
+ int piece[5];
+ } PieceSet;
+
+typedef struct
+ {
+ void (*gen_piece)(int);
+ void (*rot_piece)(int);
+ void (*do_paths)(void);
+ int special_count;
+ } GameDefinition;
+
+typedef struct
+ {
+ int x,y;
+ int level;
+ int score;
+ int rotate;
+ int is_special;
+ int current_type;
+ Piece current;
+ int next_is_special;
+ int next_type;
+ Piece next;
+ } Game;
+
+GameDefinition gamedef[GAME_TYPES]=
+ {
+ {
+ GenTetrisWirePiece,
+ RotTetrisWirePiece,
+ CheckWirePaths,
+ 40
+ },
+ {
+ GenColumnsWirePiece,
+ RotColumnsWirePiece,
+ CheckWirePaths,
+ 60
+ },
+ {
+ GenTetrisClassicPiece,
+ RotTetrisClassicPiece,
+ CheckClassicPaths,
+ 99
+ }
+ };
+
+
+/* Game piece maps - In a seperate include to maintain some semblance
+ of neatness. Include file must have these arrays :
+
+ PieceSet piece_set[NO_PIECE_SET];
+ Piece tetris_map[NO_TETRIS_PIECE];
+ int tetris_height[NO_TETRIS_PIECE];
+ {anon} tetris_off[NO_TETRIS_PIECE][4].(x|y);
+
+ Note that the columns mapping is handled in an hard coded
+ way, thanks to it's easiness.
+
+ Also note that the same maps are used for both TETRIS
+ modes, but the classic mode replaces the wiring sprites
+ with simple block sprites.
+*/
+#include "piecemap.h"
+
+
+
+/* Control vars
+*/
+int SOUND =TRUE; /* TRUE on doing sound code */
+int MUSIC =TRUE;
+int PITWIDTH=MAXPITWIDTH;
+int LEVEL =1;
+int SCORE =0;
+int MODE =TETRIS_WIRE;
+int MOTION =TRUE;
+int SLOW =FALSE;
+
+
+/* Function defs
+*/
+void TitlePage(void);
+
+void Error(char *msg);
+
+void ReadMe(void);
+
+void DefineKeys(void);
+
+void PlayGame(void);
+void GameOver(void);
+void EnterHisc(void);
+
+void SoundFX(int fx);
+
+void LoadHisc(void);
+void SaveHisc(void);
+
+void Redraw(void);
+void Melt(int x1,int y1,int x2,int y2);
+
+void InitStars(void);
+void RemoveStars(void);
+void UpdateStars(void);
+
+void InitParticles(void);
+void RemoveParticles(void);
+void UpdateParticles(void);
+void AddParticle(int x,int y,int xi,int yi);
+
+void InitScores(void);
+void AddScore(char *s,int sc);
+void UpdateScores(void);
+
+int ROL_Piece(int tile);
+int ROR_Piece(int tile);
+
+int FindPathFrom(int sx,int sy,int dir,Path *p);
+int FindPath(Path *p);
+int FindLoopFrom(int ox,int oy,int x,int y,int dir,Path *p);
+int FindLoop(Path *p);
+void GetSpecialPath(Path *p);
+void DrawPath(Path *p);
+
+void DrawGameScreen(void);
+void RedrawPit(void);
+void DrawPiece(void);
+void ErasePiece(void);
+void DrawInfo(void);
+
+int MoveOK(int x,int y);
+int AddAndCheckIsDead(int id);
+void CompressPath(void);
+void CompressBlanks(void);
+void GameRedraw(void);
+void DoPause(void);
+
+
+/* Allegro vars
+*/
+#define CX (SCREEN_W/2)
+#define CY (SCREEN_H/2)
+
+BITMAP *img;
+BITMAP *workimg;
+DATAFILE *dfile;
+PALETTE pal;
+FONT *font8;
+MIDI *title_midi,*game_midi,*score_midi;
+
+
+/* Global vars
+*/
+int quit=FALSE;
+Game game;
+
+
+
+/* -------------------------------------------------------------------------
+ MAIN
+ ------------------------------------------------------------------------- */
+
+int main(int argc,char **argv)
+
+{
+ int x,y;
+ int arg;
+ int f;
+
+ srand(getpid());
+
+ /* Parse switchs
+ */
+ arg=1;
+ while(arg<argc)
+ {
+ if (*argv[arg]=='-')
+ switch (*(argv[arg]+1))
+ {
+ case 'v':
+ printf("version : %s\n",version);
+ exit(0);
+ break;
+
+ case 'q':
+ SOUND=FALSE;
+ MUSIC=FALSE;
+ break;
+
+ case 'm':
+ MUSIC=FALSE;
+ break;
+
+ case 's':
+ SLOW=TRUE;
+ MOTION=FALSE;
+ break;
+
+ default:
+ Error("usage : hardwire [-v] [-q]");
+ break;
+ }
+ arg++;
+ }
+
+ /* Initialise Allegro
+ */
+ allegro_init();
+ install_timer();
+ install_keyboard();
+
+ /* Load in datafile
+ */
+ if (!(dfile=load_datafile("hardwire.dat")))
+ Error("Error reading datafile HARDWIRE.DAT");
+
+ memcpy(&pal,dfile[HWPAL].dat,sizeof(PALETTE));
+
+ font8=(FONT *)dfile[HWFONT].dat;
+
+ title_midi=(MIDI *)dfile[TITLEMIDI].dat;
+ game_midi=(MIDI *)dfile[GAMEMIDI].dat;
+ score_midi=(MIDI *)dfile[SCOREMIDI].dat;
+
+ /* Set up sound
+ */
+ if (SOUND)
+ if (install_sound(DIGI_AUTODETECT,MIDI_AUTODETECT,NULL)==-1)
+ Error(allegro_error);
+
+ /* Set up defined keys array
+ */
+ for(f=0;f<129;f++)
+ used_keys[f]=FALSE;
+
+ used_keys[KQUIT]=TRUE;
+ used_keys[KPAUSE]=TRUE;
+ used_keys[KREADME]=TRUE;
+ used_keys[KDEFINE]=TRUE;
+ used_keys[KTOGGLE_MODE]=TRUE;
+ used_keys[KTOGGLE_LEVEL]=TRUE;
+ used_keys[KTOGGLE_WIDTH]=TRUE;
+ used_keys[KTOGGLE_MOTION]=TRUE;
+ used_keys[KTOGGLE_MUSIC]=TRUE;
+ used_keys[KBORED]=TRUE;
+ used_keys[KLEFT]=TRUE;
+ used_keys[KRIGHT]=TRUE;
+ used_keys[KROTATER]=TRUE;
+ used_keys[KROTATEL]=TRUE;
+ used_keys[KDROP]=TRUE;
+
+ /* Set up VGA graphics mode
+ */
+ if (set_gfx_mode(GFX_AUTODETECT,320,200,0,0)<0)
+ Error(allegro_error);
+
+ img=create_bitmap(SCREEN_W,SCREEN_H);
+ workimg=create_bitmap(SCREEN_W,SCREEN_H);
+
+ /* Main control loop of game
+ */
+ clear(img);
+ set_palette(pal);
+
+ LoadHisc();
+
+ TitlePage();
+
+ while(!quit)
+ {
+ game.score=SCORE;
+ game.level=LEVEL;
+
+ for(x=0;x<PITWIDTH;x++)
+ for(y=0;y<PITDEPTH;y++)
+ {
+ pit.data[y][x]=BLANK;
+ pit.id[y][x]=0;
+ }
+
+ (*gamedef[MODE].gen_piece)(FALSE);
+ (*gamedef[MODE].gen_piece)(FALSE);
+
+ DrawGameScreen();
+ PlayGame();
+ GameOver();
+
+ if (game.score>hisc[MODE][HISC-1].score)
+ {
+ EnterHisc();
+ SaveHisc();
+ }
+
+ TitlePage();
+ }
+
+ return (0);
+}
+
+
+
+/* -------------------------------------------------------------------------
+ UTILS
+ ------------------------------------------------------------------------- */
+
+void Error(char *s)
+{
+ allegro_exit();
+ fprintf(stderr,"%s\n",s);
+ exit(1);
+}
+
+
+/* -------------------------------------------------------------------------
+ WIRE ROTATE PIECE UTILS
+ ------------------------------------------------------------------------- */
+
+int ROL_Piece(int tile)
+{
+ /* This should be in a table, but using sprite numbers for the tile
+ codes makes that not so neat
+ */
+ switch(tile)
+ {
+ case CROSS:
+ return(CROSS);
+ break;
+
+ case UPDOWN:
+ return(ACROSS);
+ break;
+
+ case ACROSS:
+ return(UPDOWN);
+ break;
+
+ case LEFTDOWN:
+ return(LEFTUP);
+ break;
+
+ case RIGHTDOWN:
+ return(LEFTDOWN);
+ break;
+
+ case LEFTUP:
+ return(RIGHTUP);
+ break;
+
+ case RIGHTUP:
+ return(RIGHTDOWN);
+ break;
+
+ default:
+ return(BLANK);
+ break;
+ }
+}
+
+
+int ROR_Piece(int tile)
+{
+ /* This should be in a table, but using sprite numbers for the tile
+ codes makes that not so neat
+ */
+ switch(tile)
+ {
+ case CROSS:
+ return(CROSS);
+ break;
+
+ case UPDOWN:
+ return(ACROSS);
+ break;
+
+ case ACROSS:
+ return(UPDOWN);
+ break;
+
+ case LEFTDOWN:
+ return(RIGHTDOWN);
+ break;
+
+ case RIGHTDOWN:
+ return(RIGHTUP);
+ break;
+
+ case LEFTUP:
+ return(LEFTDOWN);
+ break;
+
+ case RIGHTUP:
+ return(LEFTUP);
+ break;
+
+ default:
+ return(BLANK);
+ break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------
+ WIRE PATH UTILS
+ ------------------------------------------------------------------------- */
+
+int Dir(int tile,int dir)
+{
+ switch(tile) /* This should be in a table, but */
+ { /* using sprite numbers for the tile */
+ case CROSS: /* codes makes that not so neat */
+ return(dir);
+ break;
+
+ case UPDOWN:
+ if ((dir==UP)||(dir==DOWN))
+ return(dir);
+ else
+ return(NODIR);
+ break;
+
+ case ACROSS:
+ if ((dir==LEFT)||(dir==RIGHT))
+ return(dir);
+ else
+ return(NODIR);
+ break;
+
+ case LEFTDOWN:
+ if (dir==RIGHT)
+ return(DOWN);
+ else if (dir==UP)
+ return(LEFT);
+ else
+ return(NODIR);
+ break;
+
+ case RIGHTDOWN:
+ if (dir==LEFT)
+ return(DOWN);
+ else if (dir==UP)
+ return(RIGHT);
+ else
+ return(NODIR);
+ break;
+
+ case LEFTUP:
+ if (dir==RIGHT)
+ return(UP);
+ else if (dir==DOWN)
+ return(LEFT);
+ else
+ return(NODIR);
+ break;
+
+ case RIGHTUP:
+ if (dir==LEFT)
+ return(UP);
+ else if (dir==DOWN)
+ return(RIGHT);
+ else
+ return(NODIR);
+ break;
+
+ default:
+ return(NODIR);
+ break;
+ }
+}
+
+
+int FindPathFrom(int sx,int sy,int dir,Path *p)
+{
+ dir=Dir(pit.data[sy][sx],dir);
+
+ if (dir==NODIR)
+ {
+ p->len=0;
+ return (FALSE);
+ }
+
+ p->p[p->len].x=sx;
+ p->p[p->len].y=sy;
+ p->len++;
+
+ sx+=xi[dir];
+ sy+=yi[dir];
+
+ if (sy==-1)
+ {
+ return (FALSE);
+ p->len=0;
+ }
+
+ if ((sx==-1)||(sx==PITWIDTH)||(sy==PITDEPTH))
+ return (TRUE);
+ else
+ return (FindPathFrom(sx,sy,dir,p));
+}
+
+
+int FindPath(Path *p)
+{
+ int f;
+
+ p->len=0;
+
+ for(f=PITDEPTH-1;f>=0;f--)
+ {
+ if (FindPathFrom(0,f,RIGHT,p))
+ return (TRUE);
+
+ if (FindPathFrom(PITWIDTH-1,f,LEFT,p))
+ return (TRUE);
+ }
+
+ for(f=0;f<PITWIDTH;f++)
+ if (FindPathFrom(f,PITDEPTH-1,UP,p))
+ return (TRUE);
+
+ return (FALSE);
+}
+
+
+int FindLoopFrom(int ox,int oy,int x,int y,int dir,Path *p)
+{
+ dir=Dir(pit.data[y][x],dir);
+
+ if (dir==NODIR)
+ {
+ p->len=0;
+ return (FALSE);
+ }
+
+ p->p[p->len].x=x;
+ p->p[p->len].y=y;
+ p->len++;
+
+ x+=xi[dir];
+ y+=yi[dir];
+
+ if ((x==ox)&&(y==oy))
+ return (TRUE);
+ else
+ return (FindLoopFrom(ox,oy,x,y,dir,p));
+}
+
+
+int FindLoop(Path *p)
+{
+ int x,y;
+
+ p->len=0;
+
+ for(y=PITDEPTH-1;y>=0;y--)
+ for(x=0;x<PITWIDTH;x++)
+ switch(pit.data[y][x])
+ {
+ case CROSS:
+ case ACROSS:
+ case LEFTDOWN:
+ case LEFTUP:
+ if (FindLoopFrom(x,y,x,y,LEFT,p))
+ return(TRUE);
+ default:
+ break;
+ }
+
+ return(FALSE);
+}
+
+
+void GetSpecialPathFromTile(int tile,Path *p)
+{
+ int x,y;
+
+ if (tile==BLANK)
+ return;
+
+ for(x=0;x<PITWIDTH;x++)
+ for(y=0;y<PITDEPTH;y++)
+ if (pit.data[y][x]==tile)
+ {
+ p->p[p->len].x=x;
+ p->p[p->len].y=y;
+ p->len++;
+ }
+}
+
+
+void GetSpecialPath(Path *p)
+{
+ p->len=0;
+
+ if (game.y>0)
+ GetSpecialPathFromTile(pit.data[game.y-1][game.x],p);
+
+ if (game.y<(PITDEPTH-1))
+ GetSpecialPathFromTile(pit.data[game.y+1][game.x],p);
+
+ if (game.x>0)
+ GetSpecialPathFromTile(pit.data[game.y][game.x-1],p);
+
+ if (game.x<(PITWIDTH-1))
+ GetSpecialPathFromTile(pit.data[game.y][game.x+1],p);
+}
+
+
+void DrawPath(Path *p)
+{
+ int f,r;
+
+ SoundFX(PATH_FX);
+
+ for(f=0;f<p->len;f++)
+ {
+ pit.data[p->p[f].y][p->p[f].x]=PATH;
+
+ for(r=0;r<5;r++)
+ AddParticle(PITX(p->p[f].x)+RND(SPRSIZE),
+ PITY(p->p[f].y)+RND(SPRSIZE),
+ RND(5)-2,RND(5)-2);
+ }
+}
+
+
+/* -------------------------------------------------------------------------
+ SCORE UTILS
+ ------------------------------------------------------------------------- */
+
+#define DSNO 3
+#define DSFADE (SLOW ? (10) : (20))
+
+struct
+ {
+ char p[80];
+ int sc;
+ int col;
+ int cnt;
+ } dispsc[DSNO];
+
+void InitScores(void)
+{
+ int f;
+
+ for(f=0;f<DSNO;f++)
+ {
+ strcpy(dispsc[f].p,"X");
+ dispsc[f].sc=0;
+ dispsc[f].col=BLACK;
+ dispsc[DSNO-1].cnt=DSFADE;
+ }
+}
+
+
+void AddScore(char *s,int sc)
+{
+ int f;
+
+ for(f=DSNO-2;f>=0;f--)
+ dispsc[f+1]=dispsc[f];
+
+ strcpy(dispsc[0].p,s);
+ dispsc[0].sc=sc;
+ dispsc[0].col=MELTOMAX-1;
+ dispsc[0].cnt=DSFADE;
+}
+
+
+void UpdateScores(void)
+{
+ int f;
+ char s[80];
+
+ for(f=0;f<DSNO;f++)
+ {
+ sprintf(s,"%-7s %5d",dispsc[f].p,dispsc[f].sc);
+
+ textout(img,font8,s,SCOREPX,SCREEN_H-(8*f+8),dispsc[f].col);
+
+ if (dispsc[f].col!=BLACK)
+ if (--dispsc[f].cnt==0)
+ {
+ dispsc[f].cnt=DSFADE;
+ if (--dispsc[f].col==MELTOMIN)
+ dispsc[f].col=BLACK;
+ }
+ }
+}
+
+
+/* -------------------------------------------------------------------------
+ SOUND UTILS
+ ------------------------------------------------------------------------- */
+
+void SoundFX(int fx)
+{
+ if (SOUND)
+ play_sample(SAM(fx),200,128,1000,FALSE);
+}
+
+
+/* -------------------------------------------------------------------------
+ HISCORE FILE CODE
+ ------------------------------------------------------------------------- */
+
+void LoadHisc(void)
+{
+ int fd;
+ int f,r;
+
+ if ((fd=open(HISCFILE,O_RDONLY|O_BINARY))!=-1)
+ {
+ for(f=0;f<GAME_TYPES;f++)
+ for(r=0;r<HISC;r++)
+ read(fd,&hisc[f][r],sizeof(Hisc));
+ close(fd);
+ }
+}
+
+
+void SaveHisc(void)
+{
+ int fd;
+ int f,r;
+
+ if ((fd=open(HISCFILE,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,
+ S_IRUSR|S_IWUSR))!=-1)
+ {
+ for(f=0;f<GAME_TYPES;f++)
+ for(r=0;r<HISC;r++)
+ write(fd,&hisc[f][r],sizeof(Hisc));
+ close(fd);
+ }
+}
+
+
+/* -------------------------------------------------------------------------
+ GRAPHICS UTILS
+ ------------------------------------------------------------------------- */
+
+void Redraw(void)
+{
+ vsync();
+ blit(img,screen,0,0,0,0,SCREEN_W,SCREEN_H);
+}
+
+
+void Melt(int x1, int y1, int x2, int y2)
+{
+ int c,x,y,xi,yi,mx,my;
+
+ mx=x2-1;
+ my=y2-1;
+
+ for(y=y1;y<y2;y++)
+ for(x=x1;x<x2;x++)
+ if (((c=img->line[y][x])<=MELTOMAX)&&(c>MELTOMIN))
+ {
+ xi=x+RND(4)-1;
+ yi=y+RND(5)-3;
+
+ xi=MID(x1,xi,mx);
+ yi=MID(y1,yi,my);
+
+ img->line[yi][xi]=c-1;
+ }
+}
+
+
+void DrawGameScreen(void)
+{
+ int x,y;
+ RLE_SPRITE *spr;
+
+ clear(img);
+
+ if (MODE==TETRIS_CLASSIC)
+ {
+ for(x=-1;x<=PITWIDTH;x++)
+ draw_rle_sprite(img,SPR(CLASSIC_WALL),PITX(x),PITY(PITDEPTH));
+
+ for(y=0;y<PITDEPTH;y++)
+ {
+ draw_rle_sprite(img,SPR(CLASSIC_WALL),PITX(PITWIDTH),PITY(y));
+ draw_rle_sprite(img,SPR(CLASSIC_WALL),PITX(-1),PITY(y));
+ }
+ }
+ else
+ {
+ for(x=-1;x<=PITWIDTH;x++)
+ {
+ if (x==-1)
+ spr=SPR(PIT_BL);
+ else if (x==PITWIDTH)
+ spr=SPR(PIT_BR);
+ else
+ spr=SPR(PIT_BM);
+
+ draw_rle_sprite(img,spr,PITX(x),PITY(PITDEPTH));
+ }
+
+ for(y=0;y<PITDEPTH;y++)
+ {
+ if (y==0)
+ spr=SPR(PIT_TL);
+ else
+ spr=SPR(PIT_ML);
+
+ draw_rle_sprite(img,spr,PITX(-1),PITY(y));
+
+ if (y==0)
+ spr=SPR(PIT_TR);
+ else
+ spr=SPR(PIT_MR);
+
+ draw_rle_sprite(img,spr,PITX(PITWIDTH),PITY(y));
+ }
+ }
+
+ textout(img,font8,"SCORE",SCOREPX,SCOREY,WHITE);
+ textout(img,font8,"LEVEL",LEVELPX,LEVELY,WHITE);
+ textout(img,font8,"NEXT",NEXTPX,NEXTY,WHITE);
+
+ DrawInfo();
+}
+
+
+void RedrawPit(void)
+{
+ int x,y;
+
+ for(x=0;x<PITWIDTH;x++)
+ for(y=0;y<PITDEPTH;y++)
+ if (pit.data[y][x]==BLANK)
+ rectfill(img,PITX(x),PITY(y),
+ PITX(x)+SPRSIZE-1,PITY(y)+SPRSIZE-1,BLACK);
+ else
+ draw_rle_sprite(img,SPR(pit.data[y][x]),PITX(x),PITY(y));
+}
+
+
+void DrawPiece(void)
+{
+ int x,y;
+
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ if (game.current.data[y][x]!=BLANK)
+ draw_rle_sprite(img,SPR(game.current.data[y][x]),
+ PITX(game.x+x-game.current.ox),
+ PITY(game.y+y-game.current.oy));
+}
+
+
+void ErasePiece(void)
+{
+ int x,y;
+
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ if (game.current.data[y][x]!=BLANK)
+ rectfill(img,PITX(game.x+x-game.current.ox),
+ PITY(game.y+y-game.current.oy),
+ PITX(game.x+x-game.current.ox)+SPRSIZE-1,
+ PITY(game.y+y-game.current.oy)+SPRSIZE-1,
+ BLACK);
+}
+
+
+void DrawInfo(void)
+{
+ char sc[80];
+ int x,y,px,py;
+
+ sprintf(sc,"%7d",game.score);
+ textout(img,font8,sc,SCOREX,SCOREY,WHITE);
+
+ sprintf(sc,"%7d",game.level);
+ textout(img,font8,sc,LEVELX,LEVELY,WHITE);
+
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ {
+ px=NEXTX+x*SPRSIZE;
+ py=NEXTY+y*SPRSIZE;
+
+ if (game.next.data[y][x]==BLANK)
+ rectfill(img,px,py,px+SPRSIZE,py+SPRSIZE,BLACK);
+ else
+ draw_rle_sprite(img,SPR(game.next.data[y][x]),px,py);
+ }
+}
+
+
+/* -------------------------------------------------------------------------
+ STARFIELD
+ ------------------------------------------------------------------------- */
+#define NOSTAR 1000
+#define STARCUBEXY 256000 /* 64000 */
+#define STARCUBEZ 256000
+#define MAXSTINC 1000.0
+#define STARCNT 20
+
+typedef struct {
+ float x,y,z;
+ int col;
+ } Star;
+
+Star star[NOSTAR];
+
+float st_inc_x,st_inc_xi;
+float st_inc_y,st_inc_yi;
+float st_inc_z,st_inc_zi;
+unsigned int st_cnt;
+
+void InitStars(void)
+{
+ int f;
+
+ set_projection_viewport(0,0,SCREEN_W,SCREEN_H);
+
+ for(f=0;f<NOSTAR;f++)
+ {
+ star[f].x=(float)RND(STARCUBEXY*2)-STARCUBEXY;
+ star[f].y=(float)RND(STARCUBEXY*2)-STARCUBEXY;
+ star[f].z=(float)RND(STARCUBEZ);
+ star[f].col=RND(STARCOLMAX-STARCOLMIN)+STARCOLMIN;
+ }
+
+ st_inc_x=0.0;
+ st_inc_xi=40.0;
+
+ st_inc_y=0.0;
+ st_inc_yi=80.0;
+
+ st_inc_z=MAXSTINC;
+ st_inc_zi=-80.0;
+
+ st_cnt=0;
+}
+
+
+void StarPos(Star *s,int *x,int *y)
+{
+ float fx,fy;
+
+ persp_project_f(s->x,s->y,s->z,&fx,&fy);
+ *x=(int)fx;
+ *y=(int)fy;
+}
+
+
+void RemoveStars(void)
+{
+ int x,y;
+ int f;
+
+ for(f=0;f<NOSTAR;f++)
+ {
+ StarPos(star+f,&x,&y);
+
+ if (getpixel(img,x,y)==star[f].col)
+ putpixel(img,x,y,BLACK);
+ }
+}
+
+
+void UpdateStars(void)
+{
+ int x,y;
+ int f;
+
+ if (!(++st_cnt%STARCNT))
+ {
+ st_inc_x+=st_inc_xi;
+
+ if ((st_inc_x<=-MAXSTINC)||(st_inc_x>=MAXSTINC))
+ st_inc_xi=-st_inc_xi;
+
+ st_inc_y+=st_inc_yi;
+
+ if ((st_inc_y<=-MAXSTINC)||(st_inc_y>=MAXSTINC))
+ st_inc_yi=-st_inc_yi;
+
+ st_inc_z+=st_inc_zi;
+
+ if ((st_inc_z<=-MAXSTINC)||(st_inc_z>=MAXSTINC))
+ st_inc_zi=-st_inc_zi;
+ }
+
+ for(f=0;f<NOSTAR;f++)
+ {
+ StarPos(star+f,&x,&y);
+
+ if (getpixel(img,x,y)==star[f].col)
+ putpixel(img,x,y,BLACK);
+
+ star[f].x+=st_inc_x;
+
+ if (star[f].x<(float)-STARCUBEXY)
+ star[f].x+=STARCUBEXY*2;
+
+ if (star[f].x>(float)STARCUBEXY)
+ star[f].x-=STARCUBEXY*2;
+
+ star[f].y+=st_inc_y;
+
+ if (star[f].y<(float)-STARCUBEXY)
+ star[f].y+=STARCUBEXY*2;
+
+ if (star[f].y>(float)STARCUBEXY)
+ star[f].y-=STARCUBEXY*2;
+
+ star[f].z+=st_inc_z;
+
+ if (star[f].z<=0.0)
+ star[f].z+=STARCUBEZ;
+
+ if (star[f].z>(float)STARCUBEZ)
+ star[f].z-=STARCUBEZ;
+
+ StarPos(star+f,&x,&y);
+
+ if (getpixel(img,x,y)==BLACK)
+ putpixel(img,x,y,star[f].col);
+ }
+}
+
+
+/* -------------------------------------------------------------------------
+ PARTCLE ROUTINES
+ ------------------------------------------------------------------------- */
+#define PARTICLELIFE 25
+
+typedef struct Particle
+ {
+ int x,y;
+ int xi,yi;
+ int col;
+ int old_col;
+ int life;
+ struct Particle *next;
+ } Particle;
+
+Particle *part_head=NULL;
+Particle *part_tail=NULL;
+unsigned int part_cnt;
+
+void InitParticles(void)
+{
+ Particle *p;
+
+ while(part_head)
+ {
+ p=part_head;
+ part_head=part_head->next;
+ free(p);
+ }
+
+ part_head=NULL;
+ part_tail=NULL;
+}
+
+
+void RemoveParticles(void)
+{
+ Particle *p;
+
+ p=part_head;
+
+ while(p)
+ {
+ if (getpixel(img,p->x,p->y)==p->col)
+ putpixel(img,p->x,p->y,p->old_col);
+ p=p->next;
+ }
+}
+
+
+void UpdateParticles(void)
+{
+ Particle *p;
+
+ /* May as well check, as there'll be more often no particles to do
+ */
+ if (!(p=part_head))
+ return;
+
+ /* Looks naff, but it's better in seperate passes to make sure we don't
+ leave any aftifacts and make the particle routines entirely transparent.
+ Having said that it does clash with the starfield anyway!
+ */
+ while(p)
+ {
+ if (getpixel(img,p->x,p->y)==p->col)
+ putpixel(img,p->x,p->y,p->old_col);
+
+ p=p->next;
+ }
+
+ p=part_head;
+
+ while(p)
+ {
+ p->x+=p->xi;
+ p->y+=p->yi;
+ p->life--;
+ p->old_col=getpixel(img,p->x,p->y);
+
+ p=p->next;
+ }
+
+ p=part_head;
+
+ while(p)
+ {
+ if (p->life)
+ putpixel(img,p->x,p->y,p->col);
+
+ p=p->next;
+ }
+
+ /* Remove dead particles from the list
+ */
+ while((part_head)&&(part_head->life==0))
+ {
+ p=part_head;
+ part_head=part_head->next;
+ free(p);
+ }
+
+ if (!part_head)
+ part_tail=NULL;
+}
+
+
+void AddParticle(int x, int y, int xi, int yi)
+{
+ Particle *p;
+
+ if (!(p=(Particle *)malloc(sizeof(Particle))))
+ Error("malloc(Particle) failed");
+
+ if ((!xi)&&(!yi))
+ xi=1;
+
+ p->x=x;
+ p->y=y;
+ p->xi=xi;
+ p->yi=yi;
+ p->col=RND(PARTCOLMAX-PARTCOLMIN)+PARTCOLMIN;
+ p->life=PARTICLELIFE;
+ p->next=NULL;
+
+ if (part_head)
+ {
+ part_tail->next=p;
+ part_tail=p;
+ }
+ else
+ {
+ part_head=p;
+ part_tail=p;
+ }
+}
+
+
+/* -------------------------------------------------------------------------
+ DEFINE KEYS
+ ------------------------------------------------------------------------- */
+
+void DefineKeys(void)
+{
+ static struct {int *key; char *prompt;} kdef[]=
+ {
+ {&KLEFT, "Left "},
+ {&KRIGHT, "Right "},
+ {&KROTATEL,"Rotate clockwise/up "},
+ {&KROTATER,"Rotate clockwise/down "},
+ {&KDROP, "Drop/fire "},
+ {&KPAUSE, "Pause "},
+ };
+ int k;
+ int f;
+ int tx,kx,ty;
+
+ used_keys[KLEFT]=FALSE;
+ used_keys[KRIGHT]=FALSE;
+ used_keys[KROTATER]=FALSE;
+ used_keys[KROTATEL]=FALSE;
+ used_keys[KDROP]=FALSE;
+ used_keys[KPAUSE]=FALSE;
+
+ text_mode(0);
+ f=0;
+ clear(img);
+ clear_keybuf();
+
+ CENTRE("_____________",2,WHITE);
+ CENTRE("REDEFINE KEYS",0,WHITE);
+
+ while(f<6)
+ {
+ tx=10;
+ kx=10+text_length(font8,kdef[f].prompt);
+ ty=50+f*16;
+
+ textout(img,font8,kdef[f].prompt,tx,ty,YELLOW);
+
+ Redraw();
+
+ while((k=GETKEY)==-1);
+
+ *kdef[f].key=k;
+
+ rectfill(img,kx,ty,319,ty+8,BLACK);
+ textout(img,font8,keystr[k],kx,ty,WHITE);
+
+ if (used_keys[k])
+ {
+ CENTRE("KEY ALREADY USED!",SCREEN_H-8,RED);
+ Redraw();
+ }
+ else
+ {
+ CENTRE(" ",SCREEN_H-8,BLACK);
+ Redraw();
+ used_keys[k]=TRUE;
+ while (key[k]);
+ clear_keybuf();
+ f++;
+ }
+ }
+
+ while(GETKEY!=KEY_SPACE)
+ {
+ CENTRE("PRESS SPACE",SCREEN_H-8,RND(16)+1);
+ Redraw();
+ }
+
+ clear_keybuf();
+}
+
+
+/* -------------------------------------------------------------------------
+ README FILE
+ ------------------------------------------------------------------------- */
+
+void ReadMe(void)
+{
+ static char *readme[]=
+ {
+# include "readme.h"
+ NULL
+ };
+
+ char *t;
+ int l=0;
+ int p;
+ int py;
+ int f;
+ int y;
+
+ for(f=0;readme[f];f++)
+ l=f;
+
+ p=l-2;
+ py=-8;
+
+ while(GETKEY!=KEY_SPACE)
+ {
+ clear(img);
+
+ text_mode(BLACK);
+ for(y=py,f=0;y<SCREEN_H+8;f++,y+=8)
+ if (*(t=readme[(p+f)%l])!='@')
+ textout_centre(img,font8,t,CX,y,GREY3);
+ else
+ textout_centre(img,font8,t+1,CX,y,WHITE);
+
+ rectfill(img,0,0,SCREEN_W-1,8,BLUE);
+ rectfill(img,0,SCREEN_H-9,SCREEN_W-1,SCREEN_H-1,BLUE);
+
+ text_mode(BLUE);
+ textout_centre(img,font8,"CURSOR KEYS to MOVE",CX,1,BLACK);
+ textout_centre(img,font8,"SPACE to return to title page",
+ CX,SCREEN_H-8,BLACK);
+
+ Redraw();
+
+ if (key[KEY_DOWN])
+ {
+ if (SLOW)
+ py-=4;
+ else
+ py--;
+
+ if (py==-16)
+ {
+ py=-8;
+
+ if (++p==l)
+ p=0;
+ }
+ }
+ else if (key[KEY_UP])
+ {
+ if (SLOW)
+ py+=4;
+ else
+ py++;
+
+ if (py==0)
+ {
+ py=-8;
+
+ if (--p<0)
+ p=l-1;
+ }
+ }
+ }
+
+ clear(img);
+ clear_keybuf();
+
+}
+
+
+/* -------------------------------------------------------------------------
+ TITLE PAGE
+ ------------------------------------------------------------------------- */
+
+void TitlePage(void)
+{
+ static char *title[]={"(c) Noddybox 1998",
+ "Press SPACE to start",
+ "This game helped along by",
+ "The Prodigy - NiN",
+ "Suzanne Vega - Texas",
+ "FSOL - Placebo",
+ "Garbage - Meridith Brooks",
+ "Babylon 5 - Castlevania CD",
+ "This game not at ALL helped by",
+ "3Dfx - Tomb Raider II",
+ "Diddy Kong Racing - PS Castlevania",
+ "Goldeneye 64",
+ " ",
+ "Shaken, not stirred...",
+ " ",
+ "Hello to anyone who may know me",
+ " ",
+ NULL};
+
+ static char *footer[]={"Written using Allegro 3.0",
+ "Allegro written by Shawn Hargreaves",
+ NULL};
+
+ static char *instruct[]={"PRESS SPACE TO PLAY",
+ "PRESS I FOR INSTRUCTIONS",
+ "PRESS R TO REDFINE KEYS",
+ NULL};
+
+ char s[80];
+ unsigned int ctr=0;
+ int tpos=0;
+ int fpos=0;
+ int ipos=0;
+ int done=FALSE;
+ int f;
+ int hisc_mode=MODE;
+ int meltcol;
+
+ clear(img);
+ clear_keybuf();
+
+ if (SLOW)
+ text_mode(0);
+ else
+ text_mode(-1);
+
+ if (MUSIC)
+ play_midi(title_midi,TRUE);
+
+ while (!done)
+ {
+ if (SLOW)
+ clear(img);
+ else
+ Melt(0,0,SCREEN_W-1,SCREEN_H-1);
+
+ if (ctr&1)
+ meltcol=MELTOMAX;
+ else
+ meltcol=WHITE;
+
+ CENTRE(title[tpos],8,meltcol);
+ CENTRE("HARDWIRE",CY/2-8,meltcol);
+
+ sprintf(s,"GAME MODE - %s",modetxt[MODE]);
+ CENTRE(s,CY-30,meltcol);
+
+ sprintf(s,"LEVEL - %-2d WIDTH - %-2d",LEVEL,PITWIDTH);
+ CENTRE(s,CY-22,meltcol);
+
+ sprintf(s,"MOTION SICKNESS - %3s MUSIC - %3s",
+ (MOTION ? "YES" : "NO "),
+ (MUSIC ? "YES" : "NO "));
+ CENTRE(s,CY-14,meltcol);
+
+ CENTRE(instruct[ipos],CY,meltcol);
+
+ sprintf(s,"BEST %s SCORES",modetxt[hisc_mode]);
+ CENTRE(s,CY+16,meltcol);
+
+ for(f=0;f<HISC;f++)
+ {
+ sprintf(s,"%d %-3s %3d %7d", f+1,
+ hisc[hisc_mode][f].name,
+ hisc[hisc_mode][f].level,
+ hisc[hisc_mode][f].score);
+ CENTRE(s,CY+34+f*8,meltcol);
+ }
+
+ CENTRE(footer[fpos],SCREEN_H-12,WHITE);
+
+ line(img,0,SCREEN_H-2,SCREEN_W-1,SCREEN_H-2,meltcol);
+
+ ctr++;
+
+ if (!(ctr%50))
+ if (!instruct[++ipos])
+ ipos=0;
+
+ if (!(ctr%30))
+ if (!title[++tpos])
+ tpos=0;
+
+ if (!(ctr%75))
+ if (!footer[++fpos])
+ fpos=0;
+
+ if (!(ctr%150))
+ if (++hisc_mode==GAME_TYPES)
+ hisc_mode=0;
+
+ Redraw();
+
+ switch(GETKEY)
+ {
+ case KQUIT:
+ quit=TRUE;
+ done=TRUE;
+ break;
+ case KTOGGLE_MODE:
+ if (++MODE==GAME_TYPES)
+ MODE=0;
+ break;
+ case KTOGGLE_LEVEL:
+ if (++LEVEL==11)
+ LEVEL=1;
+ break;
+ case KTOGGLE_MOTION:
+ MOTION=!MOTION;
+ break;
+ case KTOGGLE_MUSIC:
+ if (SOUND)
+ {
+ MUSIC=!MUSIC;
+
+ if (MUSIC)
+ play_midi(title_midi,TRUE);
+ else
+ stop_midi();
+ }
+ break;
+ case KTOGGLE_WIDTH:
+ if (--PITWIDTH<MINPITWIDTH)
+ PITWIDTH=MAXPITWIDTH;
+ break;
+ case KREADME:
+ ReadMe();
+
+ if (SLOW)
+ text_mode(0);
+ else
+ text_mode(-1);
+
+ clear(img);
+ break;
+ case KDEFINE:
+ DefineKeys();
+
+ if (SLOW)
+ text_mode(0);
+ else
+ text_mode(-1);
+
+ clear(img);
+ break;
+ case KEY_SPACE:
+ done=TRUE;
+ break;
+ }
+ }
+
+ if (MUSIC)
+ stop_midi();
+}
+
+
+/* -------------------------------------------------------------------------
+ TETRIS WIRE SPECIFIC CODE
+ ------------------------------------------------------------------------- */
+
+void GenTetrisWirePiece(int special)
+{
+ PieceSet *ps;
+ int x,y;
+
+ game.current=game.next;
+ game.current_type=game.next_type;
+ game.is_special=game.next_is_special;
+
+ game.x=PITWIDTH/2;
+
+ if (game.is_special)
+ game.y=-1;
+ else
+ game.y=-tetris_height[game.current_type]+game.current.oy;
+
+ game.rotate=0;
+
+ game.next_is_special=special;
+
+ if (special)
+ {
+ game.next_type=-1;
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ if ((x)||(y))
+ game.next.data[y][x]=BLANK;
+ else
+ game.next.data[y][x]=SPECIAL;
+
+ game.next.ox=0;
+ game.next.oy=0;
+ }
+ else
+ {
+ game.next_type=RND(NO_TETRIS_PIECE);
+
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ if (tetris_map[game.next_type].data[y][x]==BLANK)
+ game.next.data[y][x]=BLANK;
+ else
+ {
+ ps=piece_set+tetris_map[game.next_type].data[y][x];
+ game.next.data[y][x]=ps->piece[RND(ps->no)];
+ }
+
+ game.next.ox=tetris_off[game.next_type][0].x;
+ game.next.oy=tetris_off[game.next_type][0].y;
+ }
+}
+
+
+void RotTetrisWirePiece(int dir)
+{
+ int x,y;
+ Piece p;
+
+ if (game.is_special)
+ return;
+
+ if (dir==LEFT)
+ {
+ game.rotate=ROL(game.rotate);
+
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ p.data[x][3-y]=ROL_Piece(game.current.data[y][x]);
+
+ p.ox=tetris_off[game.current_type][game.rotate].x;
+ p.oy=tetris_off[game.current_type][game.rotate].y;
+ }
+ else
+ {
+ game.rotate=ROR(game.rotate);
+
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ p.data[3-x][y]=ROR_Piece(game.current.data[y][x]);
+
+ p.ox=tetris_off[game.current_type][game.rotate].x;
+ p.oy=tetris_off[game.current_type][game.rotate].y;
+ }
+
+ game.current=p;
+}
+
+
+/* -------------------------------------------------------------------------
+ TETRIS CLASSIC SPECIFIC CODE
+ ------------------------------------------------------------------------- */
+
+void GenTetrisClassicPiece(int special)
+{
+ static int type_map[NO_TETRIS_PIECE]={BLOCK1SPR,
+ BLOCK2SPR,
+ BLOCK3SPR,
+ BLOCK4SPR,
+ BLOCK5SPR,
+ BLOCK6SPR,
+ BLOCK7SPR};
+ int x,y;
+
+ game.current=game.next;
+ game.current_type=game.next_type;
+ game.is_special=game.next_is_special;
+
+ game.x=PITWIDTH/2;
+
+ if (game.is_special)
+ game.y=-1;
+ else
+ game.y=-tetris_height[game.current_type]+game.current.oy;
+
+ game.rotate=0;
+
+ game.next_is_special=FALSE;
+
+ game.next_type=RND(NO_TETRIS_PIECE);
+
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ if (tetris_map[game.next_type].data[y][x]==BLANK)
+ game.next.data[y][x]=BLANK;
+ else
+ game.next.data[y][x]=type_map[game.next_type];
+
+ game.next.ox=tetris_off[game.next_type][0].x;
+ game.next.oy=tetris_off[game.next_type][0].y;
+}
+
+
+void RotTetrisClassicPiece(int dir)
+{
+ int x,y;
+ Piece p;
+
+ if (game.is_special)
+ return;
+
+ if (dir==LEFT)
+ {
+ game.rotate=ROL(game.rotate);
+
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ p.data[x][3-y]=game.current.data[y][x];
+
+ p.ox=tetris_off[game.current_type][game.rotate].x;
+ p.oy=tetris_off[game.current_type][game.rotate].y;
+ }
+ else
+ {
+ game.rotate=ROR(game.rotate);
+
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ p.data[3-x][y]=game.current.data[y][x];
+
+ p.ox=tetris_off[game.current_type][game.rotate].x;
+ p.oy=tetris_off[game.current_type][game.rotate].y;
+ }
+
+ game.current=p;
+}
+
+
+/* -------------------------------------------------------------------------
+ COLUMNS SPECIFIC CODE
+ ------------------------------------------------------------------------- */
+
+void GenColumnsWirePiece(int special)
+{
+ int x,y;
+
+ game.current=game.next;
+ game.current_type=game.next_type;
+ game.is_special=game.next_is_special;
+
+ game.x=PITWIDTH/2;
+
+ if (game.is_special)
+ game.y=-1;
+ else
+ game.y=-3;
+
+ game.rotate=0;
+
+ game.next.ox=0;
+ game.next.oy=0;
+
+ game.next_is_special=special;
+
+ if (special)
+ {
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ if ((x)||(y))
+ game.next.data[y][x]=BLANK;
+ else
+ game.next.data[y][x]=SPECIAL;
+ }
+ else
+ {
+ for(x=0;x<4;x++)
+ for(y=0;y<4;y++)
+ if ((x)||(y==3))
+ game.next.data[y][x]=BLANK;
+ else
+ game.next.data[y][x]=RNDPIECE;
+ }
+}
+
+
+void RotColumnsWirePiece(int dir)
+{
+ int y,t;
+
+ if (game.is_special)
+ return;
+
+ if (dir==LEFT)
+ {
+ t=game.current.data[0][0];
+
+ for(y=0;y<2;y++)
+ game.current.data[y][0]=game.current.data[y+1][0];
+
+ game.current.data[2][0]=t;
+ }
+ else
+ {
+ t=game.current.data[2][0];
+
+ for(y=2;y>0;y--)
+ game.current.data[y][0]=game.current.data[y-1][0];
+
+ game.current.data[0][0]=t;
+ }
+}
+
+
+/* -------------------------------------------------------------------------
+ PLAY GAME UTILS
+ ------------------------------------------------------------------------- */
+
+int MoveOK(int x, int y)
+{
+ int cx,cy,nx,ny;
+
+ for(cx=0;cx<4;cx++)
+ for(cy=0;cy<4;cy++)
+ if (game.current.data[cy][cx]!=BLANK)
+ {
+ nx=x+cx-game.current.ox;
+ ny=y+cy-game.current.oy;
+
+ if ((nx<0)||(nx>=PITWIDTH)||(ny>=PITDEPTH))
+ return(FALSE);
+
+ if ((ny>=0)&&(pit.data[ny][nx]!=BLANK))
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+int AddAndCheckIsDead(int id)
+{
+ int cx,cy,nx,ny;
+
+ if (game.is_special)
+ return(game.y<0);
+ else
+ for(cx=0;cx<4;cx++)
+ for(cy=0;cy<4;cy++)
+ if (game.current.data[cy][cx]!=BLANK)
+ {
+ nx=game.x+cx-game.current.ox;
+ ny=game.y+cy-game.current.oy;
+
+ if ((nx<0)||(nx>=PITWIDTH)||(ny<0)||(ny>=PITDEPTH))
+ return(TRUE);
+
+ pit.data[ny][nx]=game.current.data[cy][cx];
+ pit.id[ny][nx]=id;
+ }
+
+ return(FALSE);
+}
+
+
+void UnglueID(int id)
+{
+ int x,y;
+
+ for(x=0;x<PITWIDTH;x++)
+ for(y=0;y<PITDEPTH;y++)
+ if (pit.id[y][x]==id)
+ pit.id[y][x]=0;
+}
+
+
+void CompressPath(void)
+{
+ int x,y,ny;
+
+ for(x=0;x<PITWIDTH;x++)
+ {
+ y=PITDEPTH-1;
+
+ while(y)
+ {
+ if (pit.data[y][x]==PATH)
+ {
+ if (pit.id[y][x])
+ UnglueID(pit.id[y][x]);
+
+ for(ny=y;ny;ny--)
+ {
+ pit.data[ny][x]=pit.data[ny-1][x];
+ pit.id[ny][x]=pit.id[ny-1][x];
+
+ if (pit.id[ny][x])
+ UnglueID(pit.id[ny][x]);
+ }
+
+ pit.data[0][x]=BLANK;
+ pit.id[0][x]=0;
+ }
+ else
+ y--;
+ }
+ }
+}
+
+
+int IsDropPieceAbove(int x, int y)
+{
+ while(y>=0)
+ if (pit.data[y][x]!=BLANK)
+ return (pit.id[y][x]==0);
+ else
+ y--;
+ return(FALSE);
+}
+
+
+void CompressBlanks(void)
+{
+ int x,y,ny;
+
+ for(x=0;x<PITWIDTH;x++)
+ {
+ y=PITDEPTH-1;
+
+ while(y)
+ /* This is a bit wasteful, but will ensure that no checks are missed
+ out...
+ */
+ if ((pit.data[y][x]==BLANK)&&(IsDropPieceAbove(x,y-1)))
+ {
+ for(ny=y;(ny)&&(!pit.id[ny][x]);ny--)
+ {
+ pit.data[ny][x]=pit.data[ny-1][x];
+ pit.id[ny][x]=pit.id[ny-1][x];
+ }
+
+ if (ny!=y)
+ {
+ pit.data[ny][x]=BLANK;
+ pit.id[ny][x]=0;
+ }
+ else
+ y--;
+ }
+ else
+ y--;
+ }
+}
+
+
+void GameRedraw(void)
+{
+ if (MOTION)
+ RemoveStars();
+
+ UpdateScores();
+
+ UpdateParticles();
+
+ if (MOTION)
+ UpdateStars();
+
+ Redraw();
+}
+
+
+void DoPause(void)
+{
+ BITMAP *pause;
+ fixed rot,scale,scalei;
+
+ pause=(BITMAP *)dfile[PAUSE_BMP].dat;
+
+ blit(img,workimg,0,0,0,0,SCREEN_W,SCREEN_H);
+
+ clear_keybuf();
+
+ rot=0;
+ scale=1<<16;
+ scalei=0x0400;
+
+ while(GETKEY!=KPAUSE)
+ {
+ draw_character(img,workimg,0,0,MELTOMAX-1);
+
+ rotate_scaled_sprite(img,pause,60,70,rot<<16,scale);
+
+ if (++rot==256)
+ rot=0;
+
+ scale+=scalei;
+
+ if ((scale>=(2<<16))||(scale<=(0x4000)))
+ scalei=-scalei;
+
+ Redraw();
+ }
+
+ clear_keybuf();
+ blit(workimg,img,0,0,0,0,SCREEN_W,SCREEN_H);
+ Redraw();
+}
+
+
+/* -------------------------------------------------------------------------
+ PATH AND SCORE CHECKERS
+ ------------------------------------------------------------------------- */
+
+void CheckWirePaths(void)
+{
+ int sc;
+ Path path;
+
+ game.score+=game.level;
+
+ if (game.is_special)
+ {
+ GetSpecialPath(&path);
+
+ /* Shouldn't be false anyway...
+ */
+ if (path.len)
+ {
+ sc=(path.len*game.level);
+ game.score+=sc;
+ AddScore("SPECIAL",sc);
+ DrawPath(&path);
+ RedrawPit();
+ DrawInfo();
+ GameRedraw();
+ CompressPath();
+ RedrawPit();
+ GameRedraw();
+ CompressBlanks();
+ RedrawPit();
+ GameRedraw();
+ }
+ }
+
+ /* Check for paths
+ */
+ while (FindPath(&path))
+ {
+ sc=(path.len*game.level)*10;
+ game.score+=sc;
+ AddScore("CIRCUIT",sc);
+ DrawPath(&path);
+ RedrawPit();
+ DrawInfo();
+ GameRedraw();
+ CompressPath();
+ RedrawPit();
+ GameRedraw();
+ CompressBlanks();
+ RedrawPit();
+ GameRedraw();
+ }
+
+ /* Check for loops
+ */
+ while (FindLoop(&path))
+ {
+ sc=(path.len*game.level)*10;
+ game.score+=sc;
+ AddScore("LOOP",sc);
+ DrawPath(&path);
+ RedrawPit();
+ DrawInfo();
+ GameRedraw();
+ CompressPath();
+ RedrawPit();
+ GameRedraw();
+ CompressBlanks();
+ RedrawPit();
+ GameRedraw();
+ }
+
+}
+
+
+void CheckClassicPaths(void)
+{
+ Path path;
+ int x,y;
+ int rows;
+ int is_row;
+ int sc;
+
+ game.score+=game.level;
+
+ /* Check for rows
+ */
+ rows=0;
+ path.len=0;
+
+ for(y=0;y<PITDEPTH;y++)
+ {
+ is_row=TRUE;
+
+ for(x=0;x<PITWIDTH;x++)
+ if (pit.data[y][x]==BLANK)
+ is_row=FALSE;
+
+ if (is_row)
+ {
+ rows++;
+ for(x=0;x<PITWIDTH;x++)
+ {
+ path.p[path.len].x=x;
+ path.p[path.len].y=y;
+ path.len++;
+ }
+ }
+ }
+
+ if (rows>0)
+ {
+ sc=(game.level<<(rows-1))*10;
+ game.score+=sc;
+ switch(rows)
+ {
+ case 1:
+ AddScore("Single",sc);
+ break;
+ case 2:
+ AddScore("Double",sc);
+ break;
+ case 3:
+ AddScore("Triple",sc);
+ break;
+ case 4:
+ AddScore("TETRIS!",sc);
+ break;
+ }
+
+ DrawPath(&path);
+ RedrawPit();
+ DrawInfo();
+ GameRedraw();
+ CompressPath();
+ RedrawPit();
+ GameRedraw();
+ }
+}
+
+
+/* -------------------------------------------------------------------------
+ PLAY GAME
+ ------------------------------------------------------------------------- */
+
+void PlayGame(void)
+{
+ struct {int rot_r,rot_l,left,right,drop;} bounce;
+ int mvctr;
+ int dead;
+ int no;
+ int id;
+ int k;
+
+ bounce.rot_r=0;
+ bounce.rot_l=0;
+ bounce.left=0;
+ bounce.right=0;
+ bounce.drop=0;
+
+ no=0;
+ id=1;
+ dead=FALSE;
+ mvctr=LEVELCTR(game.level);
+
+ text_mode(0);
+ clear_keybuf();
+
+ if (MUSIC)
+ play_midi(game_midi,TRUE);
+
+ if (MOTION)
+ InitStars();
+
+ InitParticles();
+
+ InitScores();
+
+ while(!dead)
+ {
+ /* Not a switch as some are variables now
+ */
+ k=GETKEY;
+
+ if (k==KQUIT)
+ dead=TRUE;
+
+ if (k==KPAUSE)
+ DoPause();
+
+ if (k==KBORED)
+ {
+ game.level++;
+ DrawInfo();
+ }
+
+ /* See if it's time for the block to drop
+ */
+ if (!(mvctr--))
+ {
+ ErasePiece();
+
+ /* See if piece can move down
+ */
+ if (MoveOK(game.x,game.y+1))
+ {
+ SoundFX(CLICK_FX);
+ game.y++;
+ mvctr=LEVELCTR(game.level);
+ DrawPiece();
+ GameRedraw();
+ }
+ else
+ {
+ SoundFX(POP_FX);
+
+ if (AddAndCheckIsDead(id++))
+ {
+ /* If dead, setup exit of loop.
+ */
+ dead=TRUE;
+ DrawPiece();
+ }
+ else
+ {
+ /* Check for scoring and paths
+ */
+ (*gamedef[MODE].do_paths)();
+
+ /* Generate new piece and reset counters
+ */
+ (*gamedef[MODE].gen_piece)
+ ((id%gamedef[MODE].special_count)==0);
+
+ bounce.rot_r=0;
+ bounce.rot_l=0;
+ bounce.left=0;
+ bounce.right=0;
+ bounce.drop=0;
+
+ if (++no==(game.level*5))
+ {
+ game.level++;
+ no=0;
+ }
+
+ mvctr=LEVELCTR(game.level);
+
+ RedrawPit();
+ DrawInfo();
+ GameRedraw();
+ }
+ }
+ }
+ else
+ /* If it's not block dropping time, allow it to move
+ */
+ {
+ ErasePiece();
+
+ /* Check left/right
+ */
+ if ((key[KLEFT])&&(!bounce.left))
+ {
+ bounce.left=BOUNCEVAL;
+
+ if (MoveOK(game.x-1,game.y))
+ game.x--;
+ }
+ else if ((key[KRIGHT])&&(!bounce.right))
+ {
+ bounce.right=BOUNCEVAL;
+
+ if (MoveOK(game.x+1,game.y))
+ game.x++;
+ }
+
+ if ((key[KDROP])&&(!bounce.drop))
+ {
+ bounce.drop=BOUNCEVAL;
+ mvctr=0;
+ }
+
+ if ((key[KROTATEL])&&(!bounce.rot_l))
+ {
+ (*gamedef[MODE].rot_piece)(LEFT);
+
+ if (MoveOK(game.x,game.y))
+ bounce.rot_l=BOUNCEVAL;
+ else
+ (*gamedef[MODE].rot_piece)(RIGHT);
+ }
+ else if ((key[KROTATER])&&(!bounce.rot_r))
+ {
+ (*gamedef[MODE].rot_piece)(RIGHT);
+
+ if (MoveOK(game.x,game.y))
+ bounce.rot_r=BOUNCEVAL;
+ else
+ (*gamedef[MODE].rot_piece)(LEFT);
+ }
+
+ DrawPiece();
+ GameRedraw();
+ }
+
+ /* Bounce keys
+ */
+ KEYBOUNCE(KROTATEL,bounce.rot_l);
+ KEYBOUNCE(KROTATER,bounce.rot_r);
+ KEYBOUNCE(KLEFT,bounce.left);
+ KEYBOUNCE(KRIGHT,bounce.right);
+ KEYBOUNCE(KDROP,bounce.drop);
+ }
+
+ if (MUSIC)
+ stop_midi();
+
+ SoundFX(GAMEOVER_FX);
+}
+
+
+/* -------------------------------------------------------------------------
+ GAME OVER
+ ------------------------------------------------------------------------- */
+
+void GameOver(void)
+{
+ int f;
+ int ctr;
+ int k;
+
+ clear_keybuf();
+ text_mode(-1);
+
+ /* Copy game screen to work screen and do some stuff
+ */
+ if (MOTION)
+ RemoveStars();
+
+ blit(img,workimg,0,0,0,0,SCREEN_W,SCREEN_H);
+
+ f=0;
+
+ if (SLOW)
+ ctr=20;
+ else
+ ctr=100;
+
+ while((f<(SCREEN_H/2)-1)&&(!((k=GETKEY)==KDROP))&&(!(k==KEY_SPACE)))
+ {
+ textout_centre(workimg,font8,"game over",CX,SCREEN_H/2-4,RND(255));
+
+ stretch_blit(workimg,img,f,f,SCREEN_W-f*2,SCREEN_H-f*2,
+ 0,0,SCREEN_W,SCREEN_H);
+ if (SLOW)
+ {
+ if (ctr)
+ ctr--;
+ else
+ f+=8;
+ }
+ else
+ {
+ if (ctr)
+ ctr--;
+ else
+ f+=2;
+ }
+
+ if (MOTION)
+ UpdateStars();
+
+ Redraw();
+ }
+
+}
+
+
+/* -------------------------------------------------------------------------
+ ENTER HISCORE
+ ------------------------------------------------------------------------- */
+
+void EnterHisc(void)
+{
+ static char hisc_let[]="ABCDEFGHIJKLMNOPQRSTUVXYZ. ~<>";
+
+ char s[80];
+ int sc;
+ int f;
+ int sl;
+ int sbx;
+ int done;
+ int curs;
+ int len;
+ int spr;
+ int ctr;
+ int k;
+
+ clear(img);
+ sl=strlen(hisc_let);
+ sbx=CX-text_length(font8,hisc_let)/2-3;
+ len=0;
+ done=FALSE;
+ curs=0;
+ spr=HISC_CURS1;
+ ctr=0;
+
+ /* Work out which hiscore to use
+ */
+ for(f=0,sc=-1;(f<HISC)&&(sc==-1);f++)
+ if (game.score>hisc[MODE][f].score)
+ sc=f;
+
+ for(f=HISC-1;f>sc;f--)
+ hisc[MODE][f]=hisc[MODE][f-1];
+
+ strcpy(hisc[MODE][sc].name,"");
+ hisc[MODE][sc].level=LEVEL;
+ hisc[MODE][sc].score=game.score;
+
+ if (MUSIC)
+ play_midi(score_midi,TRUE);
+
+ text_mode(0);
+
+ /* Go and do entry
+ */
+ while(!done)
+ {
+ rectfill(img,0,0,SCREEN_W-1,50,BLACK);
+
+ CENTRE(hisc_let,20,WHITE);
+
+ draw_rle_sprite(img,SPR(spr),sbx+curs*8,17);
+
+ if ((ctr++%10)==0)
+ if (++spr>HISC_CURS3)
+ spr=HISC_CURS1;
+
+ CENTRE("ENTER YOUR INITIALS",CY,CYAN);
+
+ k=GETKEY;
+
+ if ((k==KLEFT)||(k==KEY_LEFT))
+ {
+ SoundFX(CLICK_FX);
+ if (len==3)
+ {
+ if (curs==sl-2)
+ curs=sl-1;
+ else
+ curs=sl-2;
+ }
+ else
+ {
+ if (curs)
+ curs--;
+ else
+ curs=sl-1;
+ }
+ }
+
+ else if((k==KRIGHT)||(k==KEY_RIGHT))
+ {
+ SoundFX(CLICK_FX);
+ if (len==3)
+ {
+ if (curs==sl-2)
+ curs=sl-1;
+ else
+ curs=sl-2;
+ }
+ else
+ {
+ if (curs==(sl-1))
+ curs=0;
+ else
+ curs++;
+ }
+ }
+ else if ((k==KDROP)||(k==KEY_SPACE))
+ {
+ SoundFX(POP_FX);
+ switch(hisc_let[curs])
+ {
+ case '<':
+ if (len)
+ {
+ len--;
+ hisc[MODE][sc].name[len]=0;
+ }
+ break;
+
+ case '>':
+ done=TRUE;
+ break;
+
+ default:
+ hisc[MODE][sc].name[len]=hisc_let[curs];
+ hisc[MODE][sc].name[++len]=0;
+
+ if (len==3)
+ curs=sl-1;
+ break;
+ }
+ }
+
+ for(f=0;f<HISC;f++)
+ {
+ sprintf(s,"%d %-3s %3d %7d", f+1,
+ hisc[MODE][f].name,
+ hisc[MODE][f].level,
+ hisc[MODE][f].score);
+ if (f==sc)
+ CENTRE(s,150+f*8,WHITE);
+ else
+ CENTRE(s,150+f*8,RED);
+ }
+
+ if (MOTION)
+ UpdateStars();
+
+ Redraw();
+ }
+
+ if (MUSIC)
+ stop_midi();
+}