#include "cltr.h" void dr_vdi(int x,int y,int col,int x_off,int y_off); extern GAME *game; /* assembler routine zum kopieren eines blockes in den puffer */ #define WIDTH 10 /* breite des spielfeldes in bl”cken */ #define HEIGHT 20 /* h”he */ /* FIRSTCOLUMN=1, hardcoded */ #define FIRSTLINE 4 /* erste angezeigte zeile */ #define XWIDTH (WIDTH+2) /* breite des internen feldes */ #define XHEIGHT (HEIGHT+FIRSTLINE+1) /* h”he */ static int feld[XHEIGHT][XWIDTH]; void tetris_preview(void); void dr_tstat(int x,int y,int dy); uint do_tetris(void); GAME tetris={ "Tetris", tetris_preview, dr_tstat, do_tetris, WIDTH, HEIGHT, FIRSTLINE, 5, 5 }; /* beschreibung der spielsteine fr ausgabe */ typedef struct { /* relative positions-offsets (in bl”cken) */ int x[4]; int y[4]; } DIR; typedef struct { int dirs; /* zahl m”glicher orientierungen */ DIR dir[4]; } STONES; /* unused XXXX XXX XXX XXX XX XX XX X X X XX XX XX */ static STONES tet[]={ { 0,{{{0,0,0,0},{0,0,0,0}},{{0,0,0,0},{0,0,0,0}},{{0,0,0,0},{0,0,0,0}},{{0,0,0,0},{0,0,0,0}}} }, { 2,{{{-2,-1,0,1},{0,0,0,0}},{{0,0,0,0},{-2,-1,0,1}},{{0,0,0,0},{0,0,0,0}},{{0,0,0,0},{0,0,0,0}}} }, { 4,{{{-1,0,1,1},{0,0,0,1}},{{1,0,0,0},{-1,-1,0,1}},{{-1,-1,0,1},{-1,0,0,0}},{{0,0,0,-1},{-1,0,1,1}}} }, { 4,{{{-1,0,1,1},{0,0,0,-1}},{{-1,0,0,0},{-1,-1,0,1}},{{-1,-1,0,1},{1,0,0,0}},{{0,0,0,1},{-1,0,1,1}}} }, { 4,{{{-1,0,0,1},{0,0,1,0}},{{0,0,1,0},{-1,0,0,1}},{{-1,0,0,1},{0,0,-1,0}},{{0,0,-1,0},{-1,0,0,1}}} }, { 2,{{{-1,0,0,1},{1,1,0,0}},{{0,0,1,1},{-1,0,0,1}},{{0,0,0,0},{0,0,0,0}},{{0,0,0,0},{0,0,0,0}}} }, { 2,{{{-1,0,0,1},{0,0,1,1}},{{1,1,0,0},{-1,0,0,1}},{{0,0,0,0},{0,0,0,0}},{{0,0,0,0},{0,0,0,0}}} }, { 1,{{{0,1,0,1},{0,0,1,1}},{{0,0,0,0},{0,0,0,0}},{{0,0,0,0},{0,0,0,0}},{{0,0,0,0},{0,0,0,0}}} } }; /* beschreibung eines steines */ typedef struct { int x; int y; int typ; int dir; } STONE; static STONE stone,next; /* l”sche stein in puffer */ static void rm_stone(void) { int i,y; for ( i=0; i<4; i++ ) { y=stone.y+tet[stone.typ].dir[stone.dir].y[i]; if ( y>=FIRSTLINE ) { tetris.dr_block(stone.x+tet[stone.typ].dir[stone.dir].x[i],y,0); } } } /* zeichne stein in puffer */ static void dr_stone(void) { int i,y; DIR *dir; dir=&tet[stone.typ].dir[stone.dir]; for ( i=0; i<4; i++ ) { y=stone.y+dir->y[i]; if ( y>=FIRSTLINE ) { tetris.dr_block(stone.x+dir->x[i],y,stone.typ); } } } /*------------------------------------------------------------------------------ preview ------------------------------------------------------------------------------*/ #define PREV_X 3 #define PREV_Y FIRSTLINE+2 void tetris_preview(void) { int i; DIR *dir=&tet[next.typ].dir[next.dir]; for ( i=0; i<4; i++ ) dr_vdi(PREV_X+dir->x[i],PREV_Y+dir->y[i],next.typ,prev_x+(prev_w-tetris.block_w*4)/2,prev_y+(prev_h-tetris.block_h*4)/2); } /*------------------------------------------------------------------------------ statistik ------------------------------------------------------------------------------*/ void dr_tstat(int x,int y,int dy) { int i; y+=dy/2; x-=tetris.block_w*2; for ( i=1; i<8; i++,y+=dy ) dr_vdi(2,FIRSTLINE,i,x,y ); } /*------------------------------------------------------------------------------ spiel initialisieren ------------------------------------------------------------------------------*/ static void init_feld(void) { int i,j; for ( i=0; i7 ) feld[i][j]=0; else tetris.dr_block(j,i,feld[i][j]); } } copy_out(); } /* erzeuge neuen stein */ static void init_stone(void) { next.x=XWIDTH/2; next.y=2; next.typ=(int)(xrandom()%7+1); next.dir=(int)(xrandom()%tet[next.typ].dirs); } /*------------------------------------------------------------------------------ position der steine testen COLUMNS != TETRIS tetris: mache kopie des akt. steines mit neuer pos. teste neue pos ------------------------------------------------------------------------------*/ /* teste ob stein 'stone' an die angeg. pos darf */ static int check_pos(STONE *stone) { int i; DIR *dir; dir=&tet[stone->typ].dir[stone->dir]; for ( i=0; i<4; i++ ) { if ( feld[stone->y+dir->y[i]][stone->x+dir->x[i]]!=0 ) return 1; } return 0; } /* teste ob nichtsichbarer oberer rand leer falls nicht -> game over */ static int xcheck(void) { int i,j; for ( i=0; i0 do_sound(S_REMOVE); #endif for ( ii=8; ii<=tetris.last_flash; ii++ ) { init_wait(); for ( i=FIRSTLINE; i=FIRSTLINE; ii--,i-- ) { while ( flags[i][j]!=0 ) i--; if ( i<0 ) i=0; if ( i!=ii ) { feld[ii][j]=feld[i][j]; tetris.dr_block(j,ii,feld[ii][j]); } } } copy_out(); } return done; } /* haupt-spielroutine returns 0 -> abbruch, sonst score */ uint do_tetris(void) { int key; long time; int draw,let_it_fall; STONE x; int i; uint level,delay; uint stones,lines,score; DIR *dir; uint stat[7]={0,0,0,0,0,0,0}; int drop_line; init_feld(); check_feld(&lines); level=opts.opts[PM_TETRIS].start_level; delay=calc_delay(level); stones=lines=score=0; init_stone(); while ( 1 ) { stone=next; init_stone(); #if SOUND_LEVEL>1 do_sound(S_NEW); #endif if ( preview_flag ) redraw_preview(0l); stat[stone.typ-1]++; dr_stat(stat); if ( check_pos(&stone) || xcheck() ) break; /* stein checken */ x=stone; /* stein merken */ stones++; if ( level<9 && lines>level*10+10 ) { level++; inv_feld(); delay=calc_delay(level); } dr_score(level,lines,stones,score); let_it_fall=0; /* nicht fallen lassen */ time=gettime(); /* zeit merken */ draw=1; /* zeichnen */ while ( 1 ) { if ( draw ) { /* nur wenn n”tig */ dr_stone(); /* stein zeichnen */ draw=0; /* flag l”schen */ copy_out(); } if ( !let_it_fall ) /* falls fallen lassen */ key=get_key(); else key=0; /* kein zeichen einlesen */ switch ( key ) { case K_QUIT: return 0; /* beenden */ /*break;*/ case K_LEFT: x.x--; /* links */ if ( check_pos(&x) ) { x.x++; #if SOUND_LEVEL>3 do_sound(S_CANT_LEFT); } else { do_sound(S_LEFT); #endif } break; case K_RIGHT: x.x++; /* rechts */ if ( check_pos(&x) ) { x.x--; #if SOUND_LEVEL>3 do_sound(S_CANT_RIGHT); } else { do_sound(S_RIGHT); #endif } break; case K_ROTATE: { int h=x.dir; x.dir++; if ( x.dir>=tet[x.typ].dirs ) x.dir=0; if ( check_pos(&x) ) { x.dir=h; #if SOUND_LEVEL>3 do_sound(S_CANT_ROTATE); } else { do_sound(S_ROTATE); #endif } } break; case K_ROT_CCW: { int h=x.dir; x.dir--; if ( x.dir<0 ) x.dir=tet[x.typ].dirs-1; if ( check_pos(&x) ) { x.dir=h; #if SOUND_LEVEL>3 do_sound(S_CANT_ROT_CCW); } else { do_sound(S_ROT_CCW); #endif } } break; case K_DROP: let_it_fall=1; /* fallen lassen */ drop_line=stone.y; #if SOUND_LEVEL>2 do_sound(S_DROP); #endif break; case K_LEVEL: if ( level<9 ) { level++; inv_feld(); delay=calc_delay(level); dr_score(level,lines,stones,score); } break; } if ( gettime()>time+delay || (let_it_fall && gettime()>time+1) ) { x.y++; if ( check_pos(&x) )/* eins tiefer */ { x.y--; break; } #if SOUND_LEVEL>2 if ( let_it_fall ) do_sound(S_DROPDOWN); else do_sound(S_STEPDOWN); #endif time=gettime(); } if ( memcmp(&x,&stone,sizeof(x)) ) { draw=1; /* falls sich was getan hat */ rm_stone(); /* stein neuzeichnen */ stone=x; } } #if SOUND_LEVEL>1 do_sound(S_DOWN); #endif if ( memcmp(&x,&stone,sizeof(x)) ) { rm_stone(); /* stein neuzeichnen */ stone=x; dr_stone(); copy_out(); } /* stein ist unten angekommen */ /* eintragen ins feld */ dir=&tet[stone.typ].dir[stone.dir]; for ( i=0; i<4; i++ ) feld[stone.y+dir->y[i]][stone.x+dir->x[i]]=stone.typ; /* steine wegr„umen */ check_feld(&lines); score+=5+level*2; if ( let_it_fall ) score+=XHEIGHT-1-drop_line; else score+=XHEIGHT-1-stone.y; if ( preview_flag ) score-=3; /* tastaturpuffer l”schen */ clr_keys(); } dr_score(level,lines,stones,score); return score; }