From b2f86e20e052d9923523e29743172ea98266d28f Mon Sep 17 00:00:00 2001 From: Ian C Date: Tue, 25 Jun 2019 18:37:47 +0000 Subject: Added original Hardwire code. --- hardwire/hardwire.c | 2720 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2720 insertions(+) create mode 100644 hardwire/hardwire.c (limited to 'hardwire/hardwire.c') 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 +#include +#include +#include +#include +#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(arghisc[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;flen=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;xp[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;flen;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=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;fline[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;yx,s->y,s->z,&fx,&fy); + *x=(int)fx; + *y=(int)fy; +} + + +void RemoveStars(void) +{ + int x,y; + int f; + + for(f=0;f=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(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;ypiece[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=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=(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;y0) + { + 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;(fhisc[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