13 .float tetris_on, tet_gameovertime, tet_drawtime, tet_autodown;
15 .float piece_type, next_piece, tet_score, tet_lines;
17 var float tet_high_score = 0;
22 float TET_BORDER = 132;
23 float TET_BLOCKS = 132; // +1 = first color, +2, +3;
24 float TET_SPACE = 32; // blankness
29 float TETKEY_DOWN = 2;
30 float TETKEY_LEFT = 4;
31 float TETKEY_RIGHT = 8;
32 float TETKEY_ROTLEFT = 16;
33 float TETKEY_ROTRIGHT = 32;
34 float TETKEY_DROP = 64;
38 .float line1, line2, line3, line4, line5, line6, line7,
39 line8, line9, line10, line11, line12, line13, line14, line15,
40 line16, line17, line18, line19, line20;
43 float SVC_CENTERPRINTa = 26;
47 return ((floor((self.tet_lines / 20)) + 1));
50 void tetsnd(string snd)
52 play2(self, strcat("sounds/tetris/", snd));
56 *********************************
60 *********************************
62 void SetLine(float ln, float vl)
106 float GetLine(float ln)
152 float GetXBlock(float x, float dat)
157 return (dat & 12) / 4;
159 return (dat & 48) / 16;
161 return (dat & 192) / 64;
163 return (dat & 768) / 256;
165 return (dat & 3072) / 1024;
167 return (dat & 12288) / 4096;
169 return (dat & 49152) / 16384;
171 return (dat & 196608) / 65536;
173 return (dat & 786432) / 262144;
178 float SetXBlock(float x, float dat, float new)
181 return (dat - (dat & 3)) | new;
183 return (dat - (dat & 12)) | (new*4);
185 return (dat - (dat & 48)) | (new*16);
187 return (dat - (dat & 192)) | (new*64);
189 return (dat - (dat & 768)) | (new*256);
191 return (dat - (dat & 3072)) | (new*1024);
193 return (dat - (dat & 12288)) | (new*4096);
195 return (dat - (dat & 49152)) | (new*16384);
197 return (dat - (dat & 196608)) | (new*65536);
199 return (dat - (dat & 786432)) | (new*262144);
205 float GetSquare(float x, float y)
207 return GetXBlock(x, GetLine(y));
210 void SetSquare(float x, float y, float val)
215 dat = SetXBlock(x, dat, val & 3);
221 vector PieceShape(float pc)
230 return '5 5 2'; // 1 * 4 + 1 * 16
285 // do x 1..4 and y 1..4 in case of rotation
286 float PieceMetric(float x, float y, float rot, float pc)
292 // return bits of a piece
293 piece_dat = PieceShape(pc);
294 wid = piece_dat_z + 1;
295 if (rot == 1) // 90 degrees
301 else if (rot == 2)//180
306 else if (rot == 3) // 270
312 if (x < 1 || y < 1 || x > 4 || y > 2)
315 return GetXBlock(x, piece_dat_x); // first row
317 return GetXBlock(x, piece_dat_y); // second row
319 return GetXBlock(x, piece_dat_z); // third row (doesn't exist)
321 return 0; // illegal parms
324 *********************************
328 *********************************
332 /* some prydon gate functions to make life easier....
334 somewhat modified because we don't need all the fanciness Prydon Gate is capable of
338 // remove warnings by adding underscores, i hate warnings, warnings suck
340 void p6(float _c1, float _c2, float _c3, float _c4, float c5, float c6)
342 WriteChar(MSG_ONE, _c1);
343 WriteChar(MSG_ONE, _c2);
344 WriteChar(MSG_ONE, _c3);
345 WriteChar(MSG_ONE, _c4);
346 WriteChar(MSG_ONE, c5);
347 WriteChar(MSG_ONE, c6);
350 float pnum(float num, float dig)
355 WriteChar(MSG_ONE, 45);
359 num = num - (f * 10);
361 dig = pnum(f, dig+1);
365 for (i = 0; i < (5 - dig); i = i + 1)
366 WriteChar(MSG_ONE, TET_SPACE);
368 WriteChar(MSG_ONE, 48 + num);
372 void DrawLine(float ln)
375 WriteChar(MSG_ONE, TET_BORDER);
377 for (x = 1; x <= TET_WIDTH; x = x + 1)
379 d = GetSquare(x, ln);
381 WriteChar(MSG_ONE, TET_BLOCKS + d);
383 WriteChar(MSG_ONE, TET_SPACE);
385 WriteChar(MSG_ONE, TET_BORDER);
388 void DrawPiece(float pc, float ln)
390 float x, d, piece_ln, pcolor;
393 if (pcolor == 0) // 4
395 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
397 piece_dat = PieceShape(pc);
399 piece_ln = piece_dat_x;
401 piece_ln = piece_dat_y;
402 for (x = 1; x <= 4; x = x + 1)
404 d = GetXBlock(x, piece_ln) * pcolor;
406 WriteChar(MSG_ONE, TET_BLOCKS + d);
408 WriteChar(MSG_ONE, TET_SPACE);
410 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
416 WriteChar(MSG_ONE, SVC_CENTERPRINTa);
418 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
419 WriteChar(MSG_ONE, TET_BORDER);
420 p6(' ', ' ', ' ', ' ', ' ', ' ');
421 WriteChar(MSG_ONE, 10);
422 for (i = 1; i <= TET_LINES; i = i + 1)
424 if(self.tetris_on == 2 && i == 11)
426 p6(' ', ' ', ' ', ' ', ' ', ' ');
427 p6(' ', 'G', 'A', 'M', 'E', ' ');
428 p6(' ', 'O', 'V', 'E', 'R', ' ');
429 WriteChar(MSG_ONE, 10);
434 p6(' ', 'N', 'E', 'X', 'T', ' ');
436 DrawPiece(self.next_piece, 1);
438 DrawPiece(self.next_piece, 2);
440 p6(' ', 'L', 'I', 'N', 'E', 'S');
442 pnum(self.tet_lines, 0);
444 p6(' ', 'S', 'C', 'O', 'R', 'E');
446 pnum(self.tet_score, 0);
448 p6(' ', 'H', 'I', 'G', 'H', ' ');
450 p6(' ', 'S', 'C', 'O', 'R', 'E');
452 pnum(tet_high_score, 0);
454 p6(' ', 'L', 'E', 'V', 'E', 'L');
456 pnum(Tetris_Level(), 0);
458 p6(' ', ' ', ' ', ' ', ' ', ' ');
459 WriteChar(MSG_ONE, 10);
463 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
464 WriteChar(MSG_ONE, TET_BORDER);
465 p6(' ', ' ', ' ', ' ', ' ', ' ');
466 WriteChar(MSG_ONE, 10);
467 WriteChar(MSG_ONE, 0);
470 *********************************
474 *********************************
482 for (i=1; i<=TET_LINES; i = i + 1)
484 self.piece_pos = '0 0 0';
486 self.next_piece = self.tet_lines = self.tet_score = 0;
492 centerprint(self, "");
495 self.movetype = MOVETYPE_WALK;
496 stuffcmd(self, "loadfont user1 gfx/vera-sans\n");
502 *********************************
506 *********************************
510 return floor(random() * PIECES) + 1;
513 void TetAddScore(float n)
515 self.tet_score = self.tet_score + n * Tetris_Level();
516 if (self.tet_score > tet_high_score)
517 tet_high_score = self.tet_score;
519 float CheckMetrics(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
521 // check to see if the piece, if moved to the locations will overlap
524 // why did I start counting from 1, damnit
528 for (y = 1; y < 5; y = y + 1)
530 for (x = 1; x < 5; x = x + 1)
532 if (PieceMetric(x, y, rot, piece))
534 if (GetSquare(x + orgx, y + orgy))
535 return FALSE; // uhoh, gonna hit something.
536 if (x+orgx<1 || x+orgx > TET_WIDTH || y+orgy<1 || y+orgy> TET_LINES)
537 return FALSE; // ouside the level
544 void ClearPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
548 // why did I start counting from 1, damnit
552 for (y = 1; y < 5; y = y + 1)
554 for (x = 1; x < 5; x = x + 1)
556 if (PieceMetric(x, y, rot, piece))
558 SetSquare(x + orgx, y + orgy, 0);
563 void CementPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
567 // why did I start counting from 1, damnit
572 if (pcolor == 0) // 4
575 for (y = 1; y < 5; y = y + 1)
577 for (x = 1; x < 5; x = x + 1)
579 if (PieceMetric(x, y, rot, piece))
581 SetSquare(x + orgx, y + orgy, pcolor);
587 float LINE_LOW = 349525;
588 float LINE_HIGH = 699050; // above number times 2
590 void CompletedLines()
592 float y, cleared, ln;
599 if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)
600 cleared = cleared + 1;
603 ln = GetLine(y - cleared);
612 self.tet_lines = self.tet_lines + cleared;
613 TetAddScore(cleared * cleared * 10);
616 void HandleGame(float keyss)
619 // first off, we need to see if we need a new piece
628 if (self.piece_type == 0)
630 self.piece_pos = '5 1 0'; // that's about middle top, we count from 1 ARGH
632 self.piece_type = self.next_piece;
634 self.piece_type = RandomPiece();
635 self.next_piece = RandomPiece();
636 keyss = 0; // no movement first frame
637 self.tet_autodown = time + 0.2;
641 ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
643 // next we need to check the piece metrics against what's on the level
644 // based on the key order
646 old_pos = check_pos = self.piece_pos;
650 if (keyss & TETKEY_RIGHT)
652 check_pos_x = check_pos_x + 1;
655 else if (keyss & TETKEY_LEFT)
657 check_pos_x = check_pos_x - 1;
660 else if (keyss & TETKEY_ROTRIGHT)
662 check_pos_z = check_pos_z + 1;
663 piece_data = PieceShape(self.piece_type);
664 nudge = piece_data_z - 2;
667 else if (keyss & TETKEY_ROTLEFT)
669 check_pos_z = check_pos_z - 1;
670 piece_data = PieceShape(self.piece_type);
671 nudge = piece_data_z - 2;
677 else if (check_pos_z < 0)
681 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
682 self.piece_pos = check_pos;
686 self.tet_gameovertime = time + 2;
691 for(i = 1; i <= nudge; ++i)
693 if(CheckMetrics(self.piece_type, check_pos_x + i, check_pos_y, check_pos_z))
695 self.piece_pos = check_pos + '1 0 0' * i;
698 else if(CheckMetrics(self.piece_type, check_pos_x - i, check_pos_y, check_pos_z))
700 self.piece_pos = check_pos - '1 0 0' * i;
705 check_pos = self.piece_pos;
706 if(keyss & TETKEY_DROP)
708 // drop to bottom, but do NOT cement it yet
709 // this allows sliding it
711 while(CheckMetrics(self.piece_type, check_pos_x, check_pos_y + 1, check_pos_z))
713 self.tet_autodown = time + 1 / Tetris_Level();
715 else if (keyss & TETKEY_DOWN)
717 check_pos_y = check_pos_y + 1;
718 self.tet_autodown = time + 1 / Tetris_Level();
720 else if (self.tet_autodown < time)
722 check_pos_y = check_pos_y + 1;
723 self.tet_autodown = time + 1 / Tetris_Level();
725 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
727 if(old_pos != check_pos)
728 self.tet_drawtime = 0;
729 self.piece_pos = check_pos;
733 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
737 self.tet_drawtime = 0;
740 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
744 *********************************
746 Important Linking Into Quake stuff
748 *********************************
754 if(self.tetris_on != 1)
758 self.tet_org = self.origin;
759 self.movetype = MOVETYPE_NOCLIP;
760 stuffcmd(self, "loadfont user1 gfx/conchars\n");
770 float TetrisPreFrame()
775 self.tet_org = self.origin;
776 if (self.tet_drawtime > time)
779 self.tet_drawtime = time + 0.5;
782 float frik_anglemoda(float v)
784 return v - floor(v/360) * 360;
786 float angcompa(float y1, float y2)
788 y1 = frik_anglemoda(y1);
789 y2 = frik_anglemoda(y2);
794 answer = answer - 360;
795 else if (answer < -180)
796 answer = answer + 360;
800 .float tetkey_down, tetkey_rotright, tetkey_left, tetkey_right, tetkey_rotleft, tetkey_drop;
802 float TetrisKeyRepeat(.float fld, float f)
806 if(self.fld == 0) // initial key press
808 self.fld = time + 0.3;
811 else if(time > self.fld)
813 self.fld = time + 0.1;
818 // repeating too fast
829 float TetrisPostFrame()
837 if(self.tetris_on == 2 && time > self.tet_gameovertime)
843 self.origin = self.tet_org;
845 if(self.tetris_on == 1)
847 if(TetrisKeyRepeat(tetkey_down, self.movement_x < 0))
848 keysa |= TETKEY_DOWN;
850 if(TetrisKeyRepeat(tetkey_rotright, self.movement_x > 0))
851 keysa |= TETKEY_ROTRIGHT;
853 if(TetrisKeyRepeat(tetkey_left, self.movement_y < 0))
854 keysa |= TETKEY_LEFT;
856 if(TetrisKeyRepeat(tetkey_right, self.movement_y > 0))
857 keysa |= TETKEY_RIGHT;
859 if(TetrisKeyRepeat(tetkey_rotleft, self.BUTTON_CROUCH))
860 keysa |= TETKEY_ROTLEFT;
862 if(TetrisKeyRepeat(tetkey_drop, self.BUTTON_JUMP))
863 keysa |= TETKEY_DROP;