/* 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