13 float tet_vs_current_id;
14 float tet_vs_current_timeout;
15 .float tet_vs_id, tet_vs_addlines;
16 .float tet_highest_line;
17 .float tetris_on, tet_gameovertime, tet_drawtime, tet_autodown;
19 .float piece_type, next_piece, tet_score, tet_lines;
24 // 3 = waiting for VS players
26 var float tet_high_score = 0;
31 float TET_BORDER = 139;
32 float TET_BLOCK = 133;
33 float TET_SPACE = 160; // blankness
38 float TETKEY_DOWN = 2;
39 float TETKEY_LEFT = 4;
40 float TETKEY_RIGHT = 8;
41 float TETKEY_ROTLEFT = 16;
42 float TETKEY_ROTRIGHT = 32;
43 float TETKEY_DROP = 64;
44 string TET_PADDING_RIGHT = "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"; // get away from crosshair
48 .float line1, line2, line3, line4, line5, line6, line7,
49 line8, line9, line10, line11, line12, line13, line14, line15,
50 line16, line17, line18, line19, line20;
53 float SVC_CENTERPRINTa = 26;
57 return ((floor((self.tet_lines / 10)) + 1));
60 void tetsnd(string snd)
62 play2(self, strcat("sounds/tetris/", snd));
66 *********************************
70 *********************************
72 void SetLine(float ln, float vl)
116 float GetLine(float ln)
162 float GetXBlock(float x, float dat)
167 return (dat & 12) / 4;
169 return (dat & 48) / 16;
171 return (dat & 192) / 64;
173 return (dat & 768) / 256;
175 return (dat & 3072) / 1024;
177 return (dat & 12288) / 4096;
179 return (dat & 49152) / 16384;
181 return (dat & 196608) / 65536;
183 return (dat & 786432) / 262144;
188 float SetXBlock(float x, float dat, float new)
191 return (dat - (dat & 3)) | new;
193 return (dat - (dat & 12)) | (new*4);
195 return (dat - (dat & 48)) | (new*16);
197 return (dat - (dat & 192)) | (new*64);
199 return (dat - (dat & 768)) | (new*256);
201 return (dat - (dat & 3072)) | (new*1024);
203 return (dat - (dat & 12288)) | (new*4096);
205 return (dat - (dat & 49152)) | (new*16384);
207 return (dat - (dat & 196608)) | (new*65536);
209 return (dat - (dat & 786432)) | (new*262144);
215 float GetSquare(float x, float y)
217 return GetXBlock(x, GetLine(y));
220 void SetSquare(float x, float y, float val)
225 dat = SetXBlock(x, dat, val & 3);
231 vector PieceShape(float pc)
240 return '5 5 2'; // 1 * 4 + 1 * 16
295 // do x 1..4 and y 1..4 in case of rotation
296 float PieceMetric(float x, float y, float rot, float pc)
302 // return bits of a piece
303 piece_dat = PieceShape(pc);
304 wid = piece_dat_z + 1;
305 if (rot == 1) // 90 degrees
311 else if (rot == 2)//180
316 else if (rot == 3) // 270
322 if (x < 1 || y < 1 || x > 4 || y > 2)
325 return GetXBlock(x, piece_dat_x); // first row
327 return GetXBlock(x, piece_dat_y); // second row
329 return GetXBlock(x, piece_dat_z); // third row (doesn't exist)
331 return 0; // illegal parms
334 *********************************
338 *********************************
342 /* some prydon gate functions to make life easier....
344 somewhat modified because we don't need all the fanciness Prydon Gate is capable of
348 void WriteTetrisString(string s)
350 WriteUnterminatedString(MSG_ONE, strconv(0, 2, 2, s));
353 float pnum(float num, float dig)
358 WriteChar(MSG_ONE, 173);
362 num = num - (f * 10);
364 dig = pnum(f, dig+1);
368 for (i = 0; i < (5 - dig); i = i + 1)
369 WriteChar(MSG_ONE, TET_SPACE);
371 WriteChar(MSG_ONE, 176 + num);
375 void DrawLine(float ln)
378 WriteChar(MSG_ONE, TET_BORDER);
380 for (x = 1; x <= TET_WIDTH; x = x + 1)
382 d = GetSquare(x, ln);
385 WriteChar(MSG_ONE, '^');
386 WriteChar(MSG_ONE, d * d - 2 * d + 50); // 1, 2, 5
387 WriteChar(MSG_ONE, TET_BLOCK);
390 WriteChar(MSG_ONE, TET_SPACE);
392 WriteChar(MSG_ONE, '^');
393 WriteChar(MSG_ONE, '7');
394 WriteChar(MSG_ONE, TET_BORDER);
397 void DrawPiece(float pc, float ln)
399 float x, d, piece_ln, pcolor;
401 pcolor = mod(pc, 3) + 1;
402 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
404 piece_dat = PieceShape(pc);
406 piece_ln = piece_dat_x;
408 piece_ln = piece_dat_y;
409 for (x = 1; x <= 4; x = x + 1)
411 d = GetXBlock(x, piece_ln) * pcolor;
414 WriteChar(MSG_ONE, '^');
415 WriteChar(MSG_ONE, d * d - 2 * d + 50); // 1, 2, 5
416 WriteChar(MSG_ONE, TET_BLOCK);
419 WriteChar(MSG_ONE, TET_SPACE);
421 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
428 WriteChar(MSG_ONE, SVC_CENTERPRINTa);
430 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
431 WriteChar(MSG_ONE, TET_BORDER);
432 WriteTetrisString(" ");
433 WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
434 WriteChar(MSG_ONE, 10);
435 for (i = 1; i <= TET_LINES; i = i + 1)
437 if(self.tetris_on == 2)
438 WriteTetrisString(" GAME OVER ");
439 else if(self.tetris_on == 3)
440 WriteTetrisString("PLEASE WAIT");
444 WriteTetrisString(" NEXT ");
446 DrawPiece(self.next_piece, 1);
448 DrawPiece(self.next_piece, 2);
450 WriteTetrisString(" LINES");
452 pnum(self.tet_lines, 0);
454 WriteTetrisString(" SCORE");
456 pnum(self.tet_score, 0);
458 WriteTetrisString(" HIGH ");
460 WriteTetrisString(" SCORE");
462 pnum(tet_high_score, 0);
464 WriteTetrisString(" LEVEL");
466 pnum(Tetris_Level(), 0);
468 WriteTetrisString(" ");
469 WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
470 WriteChar(MSG_ONE, 10);
474 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
475 WriteChar(MSG_ONE, TET_BORDER);
476 WriteTetrisString(" ");
477 WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
478 WriteChar(MSG_ONE, 10);
483 WriteChar(MSG_ONE, 10);
484 WriteChar(MSG_ONE, 10);
485 if(self.tetris_on == 3)
487 WriteUnterminatedString(MSG_ONE, strcat("WAITING FOR OTHERS (", ftos(ceil(tet_vs_current_timeout - time)), " SEC)\n"));
490 WriteChar(MSG_ONE, 10);
491 FOR_EACH_REALCLIENT(head) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
494 WriteUnterminatedString(MSG_ONE, ">");
496 WriteUnterminatedString(MSG_ONE, " ");
497 if(head.tetris_on == 2)
498 WriteUnterminatedString(MSG_ONE, " X_X");
500 pnum(head.tet_highest_line, 0);
501 WriteUnterminatedString(MSG_ONE, " ");
502 WriteUnterminatedString(MSG_ONE, head.netname);
503 WriteChar(MSG_ONE, 10);
507 WriteChar(MSG_ONE, 0);
510 *********************************
514 *********************************
522 for (i=1; i<=TET_LINES; i = i + 1)
524 self.piece_pos = '0 0 0';
526 self.next_piece = self.tet_lines = self.tet_score = 0;
532 centerprint(self, " ");
536 self.movetype = MOVETYPE_WALK;
542 *********************************
546 *********************************
550 return floor(random() * PIECES) + 1;
553 void TetAddScore(float n)
555 self.tet_score = self.tet_score + n * Tetris_Level();
556 if (self.tet_score > tet_high_score)
557 tet_high_score = self.tet_score;
559 float CheckMetrics(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
561 // check to see if the piece, if moved to the locations will overlap
564 // why did I start counting from 1, damnit
568 for (y = 1; y < 5; y = y + 1)
570 for (x = 1; x < 5; x = x + 1)
572 if (PieceMetric(x, y, rot, piece))
574 if (GetSquare(x + orgx, y + orgy))
575 return FALSE; // uhoh, gonna hit something.
576 if (x+orgx<1 || x+orgx > TET_WIDTH || y+orgy<1 || y+orgy> TET_LINES)
577 return FALSE; // ouside the level
584 void ClearPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
588 // why did I start counting from 1, damnit
592 for (y = 1; y < 5; y = y + 1)
594 for (x = 1; x < 5; x = x + 1)
596 if (PieceMetric(x, y, rot, piece))
598 SetSquare(x + orgx, y + orgy, 0);
603 void CementPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
607 // why did I start counting from 1, damnit
611 pcolor = mod(piece, 3) + 1;
613 for (y = 1; y < 5; y = y + 1)
615 for (x = 1; x < 5; x = x + 1)
617 if (PieceMetric(x, y, rot, piece))
619 SetSquare(x + orgx, y + orgy, pcolor);
625 float LINE_LOW = 349525;
626 float LINE_HIGH = 699050; // above number times 2
628 void AddLines(float n)
633 FOR_EACH_REALCLIENT(head) if(head != self) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
634 head.tet_vs_addlines += n;
637 void CompletedLines()
639 float y, cleared, ln, added, pos, i;
646 if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)
647 cleared = cleared + 1;
650 ln = GetLine(y - cleared);
656 else if(cleared >= 1)
657 AddLines(cleared - 1);
659 self.tet_lines = self.tet_lines + cleared;
660 TetAddScore(cleared * cleared * 10);
662 added = self.tet_vs_addlines;
663 self.tet_vs_addlines = 0;
667 for(y = 1; y <= TET_LINES - added; ++y)
669 SetLine(y, GetLine(y + added));
671 for(y = max(1, TET_LINES - added + 1); y <= TET_LINES; ++y)
673 pos = floor(random() * TET_WIDTH);
675 for(i = 1; i <= TET_WIDTH; ++i)
677 ln = SetXBlock(i, ln, floor(random() * 3 + 1));
682 self.tet_highest_line = 0;
683 for(y = 1; y <= TET_LINES; ++y)
686 self.tet_highest_line = TET_LINES + 1 - y;
692 else if(cleared >= 4)
700 void HandleGame(float keyss)
703 // first off, we need to see if we need a new piece
712 if (self.piece_type == 0)
714 self.piece_pos = '5 1 0'; // that's about middle top, we count from 1 ARGH
716 self.piece_type = self.next_piece;
718 self.piece_type = RandomPiece();
719 self.next_piece = RandomPiece();
720 keyss = 0; // no movement first frame
721 self.tet_autodown = time + 0.2;
725 ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
727 // next we need to check the piece metrics against what's on the level
728 // based on the key order
730 old_pos = check_pos = self.piece_pos;
734 if (keyss & TETKEY_RIGHT)
736 check_pos_x = check_pos_x + 1;
739 else if (keyss & TETKEY_LEFT)
741 check_pos_x = check_pos_x - 1;
744 else if (keyss & TETKEY_ROTRIGHT)
746 check_pos_z = check_pos_z + 1;
747 piece_data = PieceShape(self.piece_type);
748 nudge = piece_data_z - 2;
751 else if (keyss & TETKEY_ROTLEFT)
753 check_pos_z = check_pos_z - 1;
754 piece_data = PieceShape(self.piece_type);
755 nudge = piece_data_z - 2;
761 else if (check_pos_z < 0)
765 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
766 self.piece_pos = check_pos;
770 self.tet_gameovertime = time + 5;
775 for(i = 1; i <= nudge; ++i)
777 if(CheckMetrics(self.piece_type, check_pos_x + i, check_pos_y, check_pos_z))
779 self.piece_pos = check_pos + '1 0 0' * i;
782 else if(CheckMetrics(self.piece_type, check_pos_x - i, check_pos_y, check_pos_z))
784 self.piece_pos = check_pos - '1 0 0' * i;
789 check_pos = self.piece_pos;
790 if(keyss & TETKEY_DROP)
792 // drop to bottom, but do NOT cement it yet
793 // this allows sliding it
795 while(CheckMetrics(self.piece_type, check_pos_x, check_pos_y + 1, check_pos_z))
797 self.tet_autodown = time + 2 / (1 + Tetris_Level());
799 else if (keyss & TETKEY_DOWN)
801 check_pos_y = check_pos_y + 1;
802 self.tet_autodown = time + 2 / (1 + Tetris_Level());
804 else if (self.tet_autodown < time)
806 check_pos_y = check_pos_y + 1;
807 self.tet_autodown = time + 2 / (1 + Tetris_Level());
809 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
811 if(old_pos != check_pos)
812 self.tet_drawtime = 0;
813 self.piece_pos = check_pos;
817 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
821 self.tet_drawtime = 0;
824 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
828 *********************************
830 Important Linking Into Quake stuff
832 *********************************
838 if(self.tetris_on == 0 || self.tetris_on == 2) // from "off" or "game over"
842 if(time < tet_vs_current_timeout)
845 self.tet_vs_id = tet_vs_current_id;
851 tet_vs_current_timeout = time + 15;
852 self.tet_vs_id = tet_vs_current_id;
853 bprint("^1TET^7R^1IS: ", self.netname, "^1 started a new game. Do ^7impulse 100^1 to join.\n");
855 self.tet_highest_line = 0;
857 self.tet_org = self.origin;
858 self.movetype = MOVETYPE_NOCLIP;
860 else if(self.tetris_on == 1) // from "on"
868 float TetrisPreFrame()
873 self.tet_org = self.origin;
874 if (self.tet_drawtime > time)
877 if(self.tetris_on == 3)
878 self.tet_drawtime = ceil(time - tet_vs_current_timeout + 0.1) + tet_vs_current_timeout;
880 self.tet_drawtime = time + 0.5;
883 float frik_anglemoda(float v)
885 return v - floor(v/360) * 360;
887 float angcompa(float y1, float y2)
889 y1 = frik_anglemoda(y1);
890 y2 = frik_anglemoda(y2);
895 answer = answer - 360;
896 else if (answer < -180)
897 answer = answer + 360;
901 .float tetkey_down, tetkey_rotright, tetkey_left, tetkey_right, tetkey_rotleft, tetkey_drop;
903 float TetrisKeyRepeat(.float fld, float f)
907 if(self.fld == 0) // initial key press
909 self.fld = time + 0.3;
912 else if(time > self.fld)
914 self.fld = time + 0.1;
919 // repeating too fast
930 float TetrisPostFrame()
939 if(self.tetris_on == 2 && time > self.tet_gameovertime)
945 if(self.tetris_on == 3 && time > tet_vs_current_timeout)
947 self.tetris_on = 1; // start VS game
948 self.tet_drawtime = 0;
951 self.origin = self.tet_org;
952 self.movetype = MOVETYPE_NONE;
954 if(self.tetris_on == 1)
956 if(TetrisKeyRepeat(tetkey_down, self.movement_x < 0))
957 keysa |= TETKEY_DOWN;
959 if(TetrisKeyRepeat(tetkey_rotright, self.movement_x > 0))
960 keysa |= TETKEY_ROTRIGHT;
962 if(TetrisKeyRepeat(tetkey_left, self.movement_y < 0))
963 keysa |= TETKEY_LEFT;
965 if(TetrisKeyRepeat(tetkey_right, self.movement_y > 0))
966 keysa |= TETKEY_RIGHT;
968 if(TetrisKeyRepeat(tetkey_rotleft, self.BUTTON_CROUCH))
969 keysa |= TETKEY_ROTLEFT;
971 if(TetrisKeyRepeat(tetkey_drop, self.BUTTON_JUMP))
972 keysa |= TETKEY_DROP;