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;
20 .float tet_piece_bucket;
25 // 3 = waiting for VS players
27 var float tet_high_score = 0;
32 float TET_BORDER = 139;
33 float TET_BLOCK = 133;
34 float TET_SPACE = 160; // blankness
39 float TETKEY_DOWN = 2;
40 float TETKEY_LEFT = 4;
41 float TETKEY_RIGHT = 8;
42 float TETKEY_ROTLEFT = 16;
43 float TETKEY_ROTRIGHT = 32;
44 float TETKEY_DROP = 64;
45 string TET_PADDING_RIGHT = "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"; // get away from crosshair
49 .float line1, line2, line3, line4, line5, line6, line7,
50 line8, line9, line10, line11, line12, line13, line14, line15,
51 line16, line17, line18, line19, line20;
54 float SVC_CENTERPRINTa = 26;
58 return ((floor((self.tet_lines / 10)) + 1));
61 void tetsnd(string snd)
63 play2(self, strcat("sounds/tetris/", snd));
67 *********************************
71 *********************************
73 void SetLine(float ln, float vl)
117 float GetLine(float ln)
163 float GetXBlock(float x, float dat)
168 return (dat & 12) / 4;
170 return (dat & 48) / 16;
172 return (dat & 192) / 64;
174 return (dat & 768) / 256;
176 return (dat & 3072) / 1024;
178 return (dat & 12288) / 4096;
180 return (dat & 49152) / 16384;
182 return (dat & 196608) / 65536;
184 return (dat & 786432) / 262144;
189 float SetXBlock(float x, float dat, float new)
192 return (dat - (dat & 3)) | new;
194 return (dat - (dat & 12)) | (new*4);
196 return (dat - (dat & 48)) | (new*16);
198 return (dat - (dat & 192)) | (new*64);
200 return (dat - (dat & 768)) | (new*256);
202 return (dat - (dat & 3072)) | (new*1024);
204 return (dat - (dat & 12288)) | (new*4096);
206 return (dat - (dat & 49152)) | (new*16384);
208 return (dat - (dat & 196608)) | (new*65536);
210 return (dat - (dat & 786432)) | (new*262144);
216 float GetSquare(float x, float y)
218 return GetXBlock(x, GetLine(y));
221 void SetSquare(float x, float y, float val)
226 dat = SetXBlock(x, dat, val & 3);
230 vector PieceShape(float pc)
235 return '1 21 0'; // J
237 return '21 1 0'; // L
239 return '85 0 0'; // I
241 return '5 20 0'; // Z
243 return '20 5 0'; // S
245 return '4 21 0'; // T
250 vector PieceCenter(float pc)
253 return '1.5 1.5 0'; // O
259 return '2.5 1.5 0'; // I
270 // do x 1..4 and y 1..4 in case of rotation
271 float PieceMetric(float x, float y, float rot, float pc)
277 // return bits of a piece
278 wid = piece_dat_z + 1;
279 piece_dat = PieceCenter(pc);
280 if (rot == 1) // 90 degrees
282 // x+cx, y+cy -> -y+cx, x+cy
283 // X, Y -> -Y+cy+cx, X-cx+cy
287 y = x - piece_dat_x + piece_dat_y;
288 x = -t + piece_dat_x + piece_dat_y;
290 else if (rot == 2)//180
292 x = 2 * piece_dat_x - x;
293 y = 2 * piece_dat_y - y;
295 else if (rot == 3) // 270
297 // x+cx, y+cy -> y+cx, -x+cy
298 // X, Y -> Y-cy+cx, -X+cx+cy
302 y = -x + piece_dat_y + piece_dat_x;
303 x = t - piece_dat_y + piece_dat_x;
305 if (x < 1 || y < 1 || x > 4 || y > 2)
307 piece_dat = PieceShape(pc);
309 return GetXBlock(x, piece_dat_x); // first row
311 return GetXBlock(x, piece_dat_y); // second row
313 return 0; // illegal parms
316 *********************************
320 *********************************
324 /* some prydon gate functions to make life easier....
326 somewhat modified because we don't need all the fanciness Prydon Gate is capable of
330 void WriteTetrisString(string s)
332 WriteUnterminatedString(MSG_ONE, strconv(0, 2, 2, s));
335 float pnum(float num, float dig)
340 WriteChar(MSG_ONE, 173);
344 num = num - (f * 10);
346 dig = pnum(f, dig+1);
350 for (i = 0; i < (5 - dig); i = i + 1)
351 WriteChar(MSG_ONE, TET_SPACE);
353 WriteChar(MSG_ONE, 176 + num);
357 void DrawLine(float ln)
360 WriteChar(MSG_ONE, TET_BORDER);
362 for (x = 1; x <= TET_WIDTH; x = x + 1)
364 d = GetSquare(x, ln);
367 WriteChar(MSG_ONE, '^');
368 WriteChar(MSG_ONE, d * d - 2 * d + 50); // 1, 2, 5
369 WriteChar(MSG_ONE, TET_BLOCK);
372 WriteChar(MSG_ONE, TET_SPACE);
374 WriteChar(MSG_ONE, '^');
375 WriteChar(MSG_ONE, '7');
376 WriteChar(MSG_ONE, TET_BORDER);
379 void DrawPiece(float pc, float ln)
381 float x, d, piece_ln, pcolor;
383 pcolor = mod(pc, 3) + 1;
384 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
386 piece_dat = PieceShape(pc);
388 piece_ln = piece_dat_x;
390 piece_ln = piece_dat_y;
391 for (x = 1; x <= 4; x = x + 1)
393 d = GetXBlock(x, piece_ln) * pcolor;
396 WriteChar(MSG_ONE, '^');
397 WriteChar(MSG_ONE, d * d - 2 * d + 50); // 1, 2, 5
398 WriteChar(MSG_ONE, TET_BLOCK);
401 WriteChar(MSG_ONE, TET_SPACE);
403 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
410 WriteChar(MSG_ONE, SVC_CENTERPRINTa);
412 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
413 WriteChar(MSG_ONE, TET_BORDER);
414 WriteTetrisString(" ");
415 WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
416 WriteChar(MSG_ONE, 10);
417 for (i = 1; i <= TET_LINES; i = i + 1)
419 if(self.tetris_on == 2)
420 WriteTetrisString(" GAME OVER ");
421 else if(self.tetris_on == 3)
422 WriteTetrisString("PLEASE WAIT");
426 WriteTetrisString(" NEXT ");
428 DrawPiece(self.next_piece, 1);
430 DrawPiece(self.next_piece, 2);
432 WriteTetrisString(" LINES");
434 pnum(self.tet_lines, 0);
436 WriteTetrisString(" SCORE");
438 pnum(self.tet_score, 0);
440 WriteTetrisString(" HIGH ");
442 WriteTetrisString(" SCORE");
444 pnum(tet_high_score, 0);
446 WriteTetrisString(" LEVEL");
448 pnum(Tetris_Level(), 0);
450 WriteTetrisString(" ");
451 WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
452 WriteChar(MSG_ONE, 10);
456 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
457 WriteChar(MSG_ONE, TET_BORDER);
458 WriteTetrisString(" ");
459 WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
460 WriteChar(MSG_ONE, 10);
465 WriteChar(MSG_ONE, 10);
466 WriteChar(MSG_ONE, 10);
467 if(self.tetris_on == 3)
469 WriteUnterminatedString(MSG_ONE, strcat("WAITING FOR OTHERS (", ftos(ceil(tet_vs_current_timeout - time)), " SEC)\n"));
472 WriteChar(MSG_ONE, 10);
473 FOR_EACH_REALCLIENT(head) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
476 WriteUnterminatedString(MSG_ONE, ">");
478 WriteUnterminatedString(MSG_ONE, " ");
479 if(head.tetris_on == 2)
480 WriteUnterminatedString(MSG_ONE, " X_X");
482 pnum(head.tet_highest_line, 0);
483 WriteUnterminatedString(MSG_ONE, " ");
484 WriteUnterminatedString(MSG_ONE, head.netname);
485 WriteChar(MSG_ONE, 10);
489 WriteChar(MSG_ONE, 0);
492 *********************************
496 *********************************
504 for (i=1; i<=TET_LINES; i = i + 1)
506 self.piece_pos = '0 0 0';
508 self.next_piece = self.tet_lines = self.tet_score = 0;
509 self.tet_piece_bucket = 0;
514 centerprint(self, " ");
518 self.movetype = MOVETYPE_WALK;
524 *********************************
528 *********************************
530 .float tet_piece_bucket;
537 if(self.tet_piece_bucket > 1)
539 p = mod(self.tet_piece_bucket, 7);
540 self.tet_piece_bucket = floor(self.tet_piece_bucket / 7);
545 p = floor(random() * 7);
548 for(i = 6; i > 0; --i)
550 q = floor(random() * i);
551 for(j = 0; j <= q; ++j)
562 self.tet_piece_bucket = b;
567 void TetAddScore(float n)
569 self.tet_score = self.tet_score + n * Tetris_Level();
570 if (self.tet_score > tet_high_score)
571 tet_high_score = self.tet_score;
573 float CheckMetrics(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
575 // check to see if the piece, if moved to the locations will overlap
578 // why did I start counting from 1, damnit
582 for (y = 0; y < 5; y = y + 1)
584 for (x = 0; x < 5; x = x + 1)
586 if (PieceMetric(x, y, rot, piece))
588 if (GetSquare(x + orgx, y + orgy))
589 return FALSE; // uhoh, gonna hit something.
590 if (x+orgx<1 || x+orgx > TET_WIDTH || y+orgy<1 || y+orgy> TET_LINES)
591 return FALSE; // ouside the level
598 void ClearPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
602 // why did I start counting from 1, damnit
606 for (y = 0; y < 5; y = y + 1)
608 for (x = 0; x < 5; x = x + 1)
610 if (PieceMetric(x, y, rot, piece))
612 SetSquare(x + orgx, y + orgy, 0);
617 void CementPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
621 // why did I start counting from 1, damnit
625 pcolor = mod(piece, 3) + 1;
627 for (y = 0; y < 5; y = y + 1)
629 for (x = 0; x < 5; x = x + 1)
631 if (PieceMetric(x, y, rot, piece))
633 SetSquare(x + orgx, y + orgy, pcolor);
639 float LINE_LOW = 349525;
640 float LINE_HIGH = 699050; // above number times 2
642 void AddLines(float n)
647 FOR_EACH_REALCLIENT(head) if(head != self) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
648 head.tet_vs_addlines += n;
651 void CompletedLines()
653 float y, cleared, ln, added, pos, i;
660 if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)
661 cleared = cleared + 1;
664 ln = GetLine(y - cleared);
670 else if(cleared >= 1)
671 AddLines(cleared - 1);
673 self.tet_lines = self.tet_lines + cleared;
674 TetAddScore(cleared * cleared * 10);
676 added = self.tet_vs_addlines;
677 self.tet_vs_addlines = 0;
681 for(y = 1; y <= TET_LINES - added; ++y)
683 SetLine(y, GetLine(y + added));
685 for(y = max(1, TET_LINES - added + 1); y <= TET_LINES; ++y)
687 pos = floor(random() * TET_WIDTH);
689 for(i = 1; i <= TET_WIDTH; ++i)
691 ln = SetXBlock(i, ln, floor(random() * 3 + 1));
696 self.tet_highest_line = 0;
697 for(y = 1; y <= TET_LINES; ++y)
700 self.tet_highest_line = TET_LINES + 1 - y;
706 else if(cleared >= 4)
714 void HandleGame(float keyss)
717 // first off, we need to see if we need a new piece
726 if (self.piece_type == 0)
728 self.piece_pos = '5 1 0'; // that's about middle top, we count from 1 ARGH
730 self.piece_type = self.next_piece;
732 self.piece_type = RandomPiece();
733 self.next_piece = RandomPiece();
734 keyss = 0; // no movement first frame
735 self.tet_autodown = time + 0.2;
739 ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
741 // next we need to check the piece metrics against what's on the level
742 // based on the key order
744 old_pos = check_pos = self.piece_pos;
748 if (keyss & TETKEY_RIGHT)
750 check_pos_x = check_pos_x + 1;
753 else if (keyss & TETKEY_LEFT)
755 check_pos_x = check_pos_x - 1;
758 else if (keyss & TETKEY_ROTRIGHT)
760 check_pos_z = check_pos_z + 1;
761 piece_data = PieceShape(self.piece_type);
765 else if (keyss & TETKEY_ROTLEFT)
767 check_pos_z = check_pos_z - 1;
768 piece_data = PieceShape(self.piece_type);
775 else if (check_pos_z < 0)
779 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
780 self.piece_pos = check_pos;
784 self.tet_gameovertime = time + 5;
789 for(i = 1; i <= nudge; ++i)
791 if(CheckMetrics(self.piece_type, check_pos_x + i, check_pos_y, check_pos_z))
792 self.piece_pos = check_pos + '1 0 0' * i;
793 else if(CheckMetrics(self.piece_type, check_pos_x - i, check_pos_y, check_pos_z))
794 self.piece_pos = check_pos - '1 0 0' * i;
800 check_pos = self.piece_pos;
801 if(keyss & TETKEY_DROP)
803 // drop to bottom, but do NOT cement it yet
804 // this allows sliding it
806 while(CheckMetrics(self.piece_type, check_pos_x, check_pos_y + 1, check_pos_z))
808 self.tet_autodown = time + 2 / (1 + Tetris_Level());
810 else if (keyss & TETKEY_DOWN)
812 check_pos_y = check_pos_y + 1;
813 self.tet_autodown = time + 2 / (1 + Tetris_Level());
815 else if (self.tet_autodown < time)
817 check_pos_y = check_pos_y + 1;
818 self.tet_autodown = time + 2 / (1 + Tetris_Level());
820 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
822 if(old_pos != check_pos)
823 self.tet_drawtime = 0;
824 self.piece_pos = check_pos;
828 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
832 self.tet_drawtime = 0;
835 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
839 *********************************
841 Important Linking Into Quake stuff
843 *********************************
849 if(self.tetris_on == 0 || self.tetris_on == 2) // from "off" or "game over"
853 if(time < tet_vs_current_timeout)
856 self.tet_vs_id = tet_vs_current_id;
862 tet_vs_current_timeout = time + 15;
863 self.tet_vs_id = tet_vs_current_id;
864 bprint("^1TET^7R^1IS: ", self.netname, "^1 started a new game. Do ^7impulse 100^1 to join.\n");
866 self.tet_highest_line = 0;
868 self.tet_org = self.origin;
869 self.movetype = MOVETYPE_NOCLIP;
871 else if(self.tetris_on == 1) // from "on"
879 float TetrisPreFrame()
884 self.tet_org = self.origin;
885 if (self.tet_drawtime > time)
888 if(self.tetris_on == 3)
889 self.tet_drawtime = ceil(time - tet_vs_current_timeout + 0.1) + tet_vs_current_timeout;
891 self.tet_drawtime = time + 0.5;
894 float frik_anglemoda(float v)
896 return v - floor(v/360) * 360;
898 float angcompa(float y1, float y2)
900 y1 = frik_anglemoda(y1);
901 y2 = frik_anglemoda(y2);
906 answer = answer - 360;
907 else if (answer < -180)
908 answer = answer + 360;
912 .float tetkey_down, tetkey_rotright, tetkey_left, tetkey_right, tetkey_rotleft, tetkey_drop;
914 float TetrisKeyRepeat(.float fld, float f)
918 if(self.fld == 0) // initial key press
920 self.fld = time + 0.3;
923 else if(time > self.fld)
925 self.fld = time + 0.1;
930 // repeating too fast
941 float TetrisPostFrame()
950 if(self.tetris_on == 2 && time > self.tet_gameovertime)
956 if(self.tetris_on == 3 && time > tet_vs_current_timeout)
958 self.tetris_on = 1; // start VS game
959 self.tet_drawtime = 0;
962 setorigin(self, self.tet_org);
963 self.movetype = MOVETYPE_NONE;
965 if(self.tetris_on == 1)
967 if(TetrisKeyRepeat(tetkey_down, self.movement_x < 0))
968 keysa |= TETKEY_DOWN;
970 if(TetrisKeyRepeat(tetkey_rotright, self.movement_x > 0))
971 keysa |= TETKEY_ROTRIGHT;
973 if(TetrisKeyRepeat(tetkey_left, self.movement_y < 0))
974 keysa |= TETKEY_LEFT;
976 if(TetrisKeyRepeat(tetkey_right, self.movement_y > 0))
977 keysa |= TETKEY_RIGHT;
979 if(TetrisKeyRepeat(tetkey_rotleft, self.BUTTON_CROUCH))
980 keysa |= TETKEY_ROTLEFT;
982 if(TetrisKeyRepeat(tetkey_drop, self.BUTTON_JUMP))
983 keysa |= TETKEY_DROP;