11 float autocvar_g_bastet;
14 float tet_vs_current_id;
15 float tet_vs_current_timeout;
16 .float tet_vs_id, tet_vs_addlines;
17 .float tet_highest_line;
18 .float tetris_on, tet_gameovertime, tet_drawtime, tet_autodown;
20 .float piece_type, next_piece, tet_score, tet_lines;
21 .float tet_piece_bucket;
26 // 3 = waiting for VS players
28 var float tet_high_score = 0;
30 vector TET_START_PIECE_POS = '5 1 0';
32 float TET_DISPLAY_LINES = 20;
35 float TET_BORDER = 139;
36 float TET_BLOCK = 133;
37 float TET_SPACE = 160; // blankness
42 float TETKEY_DOWN = 2;
43 float TETKEY_LEFT = 4;
44 float TETKEY_RIGHT = 8;
45 float TETKEY_ROTLEFT = 16;
46 float TETKEY_ROTRIGHT = 32;
47 float TETKEY_DROP = 64;
48 string TET_PADDING_RIGHT = "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"; // get away from crosshair
52 .float line1, line2, line3, line4, line5, line6, line7,
53 line8, line9, line10, line11, line12, line13, line14, line15,
54 line16, line17, line18, line19, line20, line21, line22;
57 float SVC_CENTERPRINTa = 26;
61 return ((floor((self.tet_lines / 10)) + 1));
64 void tetsnd(string snd)
66 play2(self, strcat("sounds/tetris/", snd));
70 *********************************
74 *********************************
76 void SetLine(float ln, float vl)
124 float GetLine(float ln)
174 float GetXBlock(float x, float dat)
179 return (dat & 12) / 4;
181 return (dat & 48) / 16;
183 return (dat & 192) / 64;
185 return (dat & 768) / 256;
187 return (dat & 3072) / 1024;
189 return (dat & 12288) / 4096;
191 return (dat & 49152) / 16384;
193 return (dat & 196608) / 65536;
195 return (dat & 786432) / 262144;
200 float SetXBlock(float x, float dat, float new)
203 return (dat - (dat & 3)) | new;
205 return (dat - (dat & 12)) | (new*4);
207 return (dat - (dat & 48)) | (new*16);
209 return (dat - (dat & 192)) | (new*64);
211 return (dat - (dat & 768)) | (new*256);
213 return (dat - (dat & 3072)) | (new*1024);
215 return (dat - (dat & 12288)) | (new*4096);
217 return (dat - (dat & 49152)) | (new*16384);
219 return (dat - (dat & 196608)) | (new*65536);
221 return (dat - (dat & 786432)) | (new*262144);
227 float GetSquare(float x, float y)
229 return GetXBlock(x, GetLine(y));
232 void SetSquare(float x, float y, float val)
237 dat = SetXBlock(x, dat, val & 3);
241 vector PieceShape(float pc)
244 return '20 20 0'; // O
246 return '1 21 0'; // J
248 return '16 21 0'; // L
250 return '0 85 0'; // I
252 return '5 20 0'; // Z
254 return '20 5 0'; // S
256 return '4 21 0'; // T
260 vector PieceSize(float pc)
279 vector PieceCenter(float pc)
282 return '2.5 1.5 0'; // O
288 return '2.5 2.5 0'; // I
299 // do x 1..4 and y 1..4 in case of rotation
300 float PieceMetric(float x, float y, float rot, float pc)
305 // return bits of a piece
306 ce = PieceCenter(pc);
307 if (rot == 1) // 90 degrees
309 // x+cx, y+cy -> -y+cx, x+cy
310 // X, Y -> -Y+cy+cx, X-cx+cy
315 x = -t + ce_x + ce_y;
317 else if (rot == 2)//180
322 else if (rot == 3) // 270
324 // x+cx, y+cy -> y+cx, -x+cy
325 // X, Y -> Y-cy+cx, -X+cx+cy
329 y = -x + ce_y + ce_x;
332 if (x < 1 || y < 1 || x > 4 || y > 2)
336 return GetXBlock(x, ce_x); // first row
338 return GetXBlock(x, ce_y); // second row
340 return 0; // illegal parms
342 vector tet_piecemins;
343 vector tet_piecemaxs;
344 void PieceMinsMaxs(float rot, float pc)
351 ce = PieceCenter(pc);
356 tet_piecemins_x = floor(3.0 - sz_x * 0.5);
357 tet_piecemaxs_x = floor(2.0 + sz_x * 0.5);
360 // special case for "I"
361 tet_piecemins_y = tet_piecemaxs_y = 2;
366 tet_piecemaxs_y = sz_y;
368 //print(sprintf("ce%v sz%v mi%v ma%v\n", ce, sz, tet_piecemins, tet_piecemaxs));
369 if (rot == 1) // 90 degrees
372 tet_piecemins_y = -tet_piecemins_x + ce_y + ce_x;
373 tet_piecemins_x = t - ce_y + ce_x;
375 tet_piecemaxs_y = -tet_piecemaxs_x + ce_y + ce_x;
376 tet_piecemaxs_x = t - ce_y + ce_x;
377 // swap mins_y, maxs_y
379 tet_piecemins_y = tet_piecemaxs_y;
383 else if (rot == 2)//180
386 tet_piecemins = 2 * ce - tet_piecemaxs;
387 tet_piecemaxs = 2 * ce - v;
389 else if (rot == 3) // 270
392 tet_piecemins_y = tet_piecemins_x - ce_x + ce_y;
393 tet_piecemins_x = -t + ce_x + ce_y;
395 tet_piecemaxs_y = tet_piecemaxs_x - ce_x + ce_y;
396 tet_piecemaxs_x = -t + ce_x + ce_y;
397 // swap mins_x, maxs_x
399 tet_piecemins_x = tet_piecemaxs_x;
404 print(vtos(tet_piecemins), "-");
405 print(vtos(tet_piecemaxs), "\n");
406 if(tet_piecemins_x > tet_piecemaxs_x)
407 error("inconsistent mins/maxs");
408 if(tet_piecemins_y > tet_piecemaxs_y)
409 error("inconsistent mins/maxs");
411 vector realmins, realmaxs;
414 for(i = 1; i <= 4; ++i)
415 for(j = 1; j <= 4; ++j)
416 if(PieceMetric(i, j, rot, pc))
418 realmins_x = min(realmins_x, i);
419 realmins_y = min(realmins_y, j);
420 realmaxs_x = max(realmaxs_x, i);
421 realmaxs_y = max(realmaxs_y, j);
423 if(realmins != tet_piecemins || realmaxs != tet_piecemaxs)
424 error(sprintf("incorrect mins/maxs: %v %v in %d rot %d mins %v maxs %v\n", realmins, realmaxs, rot, pc, tet_piecemins, tet_piecemaxs));
428 *********************************
432 *********************************
436 /* some prydon gate functions to make life easier....
438 somewhat modified because we don't need all the fanciness Prydon Gate is capable of
442 void WriteTetrisString(string s)
444 WriteUnterminatedString(MSG_ONE, strconv(0, 2, 2, s));
447 float pnum(float num, float dig)
452 WriteChar(MSG_ONE, 173);
456 num = num - (f * 10);
458 dig = pnum(f, dig+1);
462 for (i = 0; i < (5 - dig); i = i + 1)
463 WriteChar(MSG_ONE, TET_SPACE);
465 WriteChar(MSG_ONE, 176 + num);
469 void DrawLine(float ln)
472 WriteChar(MSG_ONE, TET_BORDER);
474 for (x = 1; x <= TET_WIDTH; x = x + 1)
476 d = GetSquare(x, ln + TET_LINES - TET_DISPLAY_LINES);
479 WriteChar(MSG_ONE, '^');
480 WriteChar(MSG_ONE, d * d - 2 * d + 50); // 1, 2, 5
481 WriteChar(MSG_ONE, TET_BLOCK);
484 WriteChar(MSG_ONE, TET_SPACE);
486 WriteChar(MSG_ONE, '^');
487 WriteChar(MSG_ONE, '7');
488 WriteChar(MSG_ONE, TET_BORDER);
491 void DrawPiece(float pc, float ln)
493 float x, d, piece_ln, pcolor;
495 pcolor = mod(pc, 3) + 1;
496 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
498 piece_dat = PieceShape(pc);
500 piece_ln = piece_dat_x;
502 piece_ln = piece_dat_y;
503 for (x = 1; x <= 4; x = x + 1)
505 d = GetXBlock(x, piece_ln) * pcolor;
508 WriteChar(MSG_ONE, '^');
509 WriteChar(MSG_ONE, d * d - 2 * d + 50); // 1, 2, 5
510 WriteChar(MSG_ONE, TET_BLOCK);
513 WriteChar(MSG_ONE, TET_SPACE);
515 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
522 WriteChar(MSG_ONE, SVC_CENTERPRINTa);
523 if(autocvar_g_bastet)
525 WriteTetrisString("NEVER GONNA GIVE YOU");
526 WriteChar(MSG_ONE, 10);
529 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
530 WriteChar(MSG_ONE, TET_BORDER);
531 WriteTetrisString(" ");
532 WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
533 WriteChar(MSG_ONE, 10);
534 for (i = 1; i <= TET_DISPLAY_LINES; i = i + 1)
536 if(self.tetris_on == 2)
537 WriteTetrisString(" GAME OVER ");
538 else if(self.tetris_on == 3)
539 WriteTetrisString("PLEASE WAIT");
543 WriteTetrisString(autocvar_g_bastet ? " THAT " : " NEXT ");
545 DrawPiece(self.next_piece, 1);
547 DrawPiece(self.next_piece, 2);
549 WriteTetrisString(" LINES");
551 pnum(self.tet_lines, 0);
553 WriteTetrisString(" SCORE");
555 pnum(self.tet_score, 0);
557 WriteTetrisString(" HIGH ");
559 WriteTetrisString(" SCORE");
561 pnum(tet_high_score, 0);
563 WriteTetrisString(" LEVEL");
565 pnum(Tetris_Level(), 0);
567 WriteTetrisString(" ");
568 WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
569 WriteChar(MSG_ONE, 10);
573 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
574 WriteChar(MSG_ONE, TET_BORDER);
575 WriteTetrisString(" ");
576 WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
577 WriteChar(MSG_ONE, 10);
582 WriteChar(MSG_ONE, 10);
583 WriteChar(MSG_ONE, 10);
584 if(self.tetris_on == 3)
586 WriteUnterminatedString(MSG_ONE, strcat("WAITING FOR OTHERS (", ftos(ceil(tet_vs_current_timeout - time)), " SEC)\n"));
589 WriteChar(MSG_ONE, 10);
590 FOR_EACH_REALCLIENT(head) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
593 WriteUnterminatedString(MSG_ONE, ">");
595 WriteUnterminatedString(MSG_ONE, " ");
596 if(head.tetris_on == 2)
597 WriteUnterminatedString(MSG_ONE, " X_X");
599 pnum(head.tet_highest_line, 0);
600 WriteUnterminatedString(MSG_ONE, " ");
601 WriteUnterminatedString(MSG_ONE, head.netname);
602 WriteChar(MSG_ONE, 10);
606 WriteChar(MSG_ONE, 0);
609 *********************************
613 *********************************
621 for (i=1; i<=TET_LINES; i = i + 1)
623 self.piece_pos = '0 0 0';
625 self.next_piece = self.tet_lines = self.tet_score = 0;
626 self.tet_piece_bucket = 0;
631 centerprint(self, " ");
635 self.movetype = MOVETYPE_WALK;
642 for(r = 1; r <= TET_LINES; ++r)
646 for(c = 1; c <= TET_WIDTH; ++c)
648 print(ftos(GetXBlock(c, l)));
654 float BastetEvaluate()
659 float occupied_count;
661 float score_save, occupied_save, occupied_count_save;
666 // adds a bonus for each free dot above the occupied blocks profile
667 occupied_count = TET_WIDTH;
670 for(i = 1; i <= TET_LINES; ++i)
679 occupied_save = occupied;
680 occupied_count_save = occupied_count;
682 for(j = 1; j <= TET_WIDTH; ++j)
686 if(!GetXBlock(j, occupied))
688 occupied = SetXBlock(j, occupied, 1);
694 score += 10000 * occupied_count;
698 occupied = occupied_save;
699 occupied_count = occupied_count_save;
700 score = score_save + 100000000 + 10000 * TET_WIDTH + 1000;
705 score += 1000 * height;
710 float CheckMetrics(float piece, float orgx, float orgy, float rot);
711 void ClearPiece(float piece, float orgx, float orgy, float rot);
712 void CementPiece(float piece, float orgx, float orgy, float rot);
713 float bastet_profile_evaluate_time;
714 float bastet_profile_checkmetrics_time;
715 float BastetSearch(float buf, float pc, float x, float y, float rot, float move_bias)
716 // returns best score, or -1 if position is impossible
724 return 0; // DO NOT WANT
726 if(x < 1 || x > TET_WIDTH || y < 1 || y > TET_LINES)
727 return -1; // impossible
731 // did we already try?
732 b = x + (TET_WIDTH+2) * (y + (TET_LINES+2) * rot);
733 r = bufstr_get(buf, b);
734 if(r != "") // already tried
737 bufstr_set(buf, b, "0"); // in case we READ that, not that bad - we already got that value in another branch then anyway
741 t1 = gettime(GETTIME_HIRES);
742 if(CheckMetrics(pc, x, y, rot))
744 t2 = gettime(GETTIME_HIRES);
745 bastet_profile_checkmetrics_time += (t2 - t1);
748 s = BastetSearch(buf, pc, x-1, y, rot, move_bias - 1); if(s > sm) sm = s;
749 s = BastetSearch(buf, pc, x+1, y, rot, move_bias - 1); if(s > sm) sm = s;
750 s = BastetSearch(buf, pc, x, y, rot+1, move_bias - 1); if(s > sm) sm = s;
751 s = BastetSearch(buf, pc, x, y, rot-1, move_bias - 1); if(s > sm) sm = s;
753 s = BastetSearch(buf, pc, x, y+1, rot, move_bias + 2); if(s > sm) sm = s;
756 //print(sprintf("MAY CEMENT AT: %d %d %d\n", x, y, rot));
757 // moving down did not work - that means we can fixate the block here
758 t1 = gettime(GETTIME_HIRES);
760 CementPiece(pc, x, y, rot);
761 s = BastetEvaluate();
762 ClearPiece(pc, x, y, rot);
764 t2 = gettime(GETTIME_HIRES);
765 bastet_profile_evaluate_time += (t2 - t1);
772 t2 = gettime(GETTIME_HIRES);
773 bastet_profile_checkmetrics_time += (t2 - t1);
774 sm = -1; // impassible
777 bufstr_set(buf, b, ftos(sm));
782 float bastet_piece[7];
783 float bastet_score[7];
784 float bastet_piecetime[7];
789 bastet_profile_evaluate_time = 0;
790 bastet_profile_checkmetrics_time = 0;
791 var float t1 = gettime(GETTIME_HIRES);
793 b = buf_create(); bastet_piece[0] = 1; bastet_score[0] = BastetSearch(b, 1, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[0]; buf_del(b);
794 b = buf_create(); bastet_piece[1] = 2; bastet_score[1] = BastetSearch(b, 2, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[1]; buf_del(b);
795 b = buf_create(); bastet_piece[2] = 3; bastet_score[2] = BastetSearch(b, 3, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[2]; buf_del(b);
796 b = buf_create(); bastet_piece[3] = 4; bastet_score[3] = BastetSearch(b, 4, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[3]; buf_del(b);
797 b = buf_create(); bastet_piece[4] = 5; bastet_score[4] = BastetSearch(b, 5, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[4]; buf_del(b);
798 b = buf_create(); bastet_piece[5] = 6; bastet_score[5] = BastetSearch(b, 6, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[5]; buf_del(b);
799 b = buf_create(); bastet_piece[6] = 7; bastet_score[6] = BastetSearch(b, 7, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[6]; buf_del(b);
801 var float t2 = gettime(GETTIME_HIRES);
802 dprint(sprintf("Time taken: %.6f seconds (of this, ev = %.2f%%, cm = %.2f%%)\n", t2 - t1, 100 * bastet_profile_evaluate_time / (t2 - t1), 100 * bastet_profile_checkmetrics_time / (t2 - t1)));
808 for(i = 0; i < 7; ++i)
810 print(sprintf("piece %s value = %d\n", substring("OJLIZST", bastet_piece[i]-1, 1), bastet_score[i]));
814 for(i = 0; i < 7; ++i)
819 for(j = i + 1; j < 7; ++j)
821 if(bastet_score[j] < s)
830 bastet_score[k] = bastet_score[i];
831 bastet_piece[k] = bastet_piece[i];
848 for(i = 0; i < 7; ++i)
851 bastet_piecetime[i] = 0.2 * bastet_piecetime[i];
853 bastet_piecetime[i] = 1.8 * bastet_piecetime[i] + 1000;
861 *********************************
865 *********************************
867 .float tet_piece_bucket;
875 if(self.tet_piece_bucket > 1)
877 p = mod(self.tet_piece_bucket, 7);
878 self.tet_piece_bucket = floor(self.tet_piece_bucket / 7);
883 p = floor(random() * 7);
886 for(i = 6; i > 0; --i)
888 q = floor(random() * i);
889 for(j = 0; j <= q; ++j)
900 self.tet_piece_bucket = b;
905 void TetAddScore(float n)
907 self.tet_score = self.tet_score + n * Tetris_Level();
908 if (self.tet_score > tet_high_score)
909 tet_high_score = self.tet_score;
911 float CheckMetrics(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
913 // check to see if the piece, if moved to the locations will overlap
916 // why did I start counting from 1, damnit
920 PieceMinsMaxs(rot, piece);
921 if (tet_piecemins_x+orgx<1 || tet_piecemaxs_x+orgx > TET_WIDTH || tet_piecemins_y+orgy<1 || tet_piecemaxs_y+orgy> TET_LINES)
922 return FALSE; // ouside the level
923 for (y = tet_piecemins_y; y <= tet_piecemaxs_y; y = y + 1)
925 l = GetLine(y + orgy);
927 for (x = tet_piecemins_x; x <= tet_piecemaxs_x; x = x + 1)
928 if (PieceMetric(x, y, rot, piece))
929 if (GetXBlock(x + orgx, l))
930 return FALSE; // uhoh, gonna hit something.
935 void ClearPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
938 // why did I start counting from 1, damnit
942 PieceMinsMaxs(rot, piece);
943 for (y = tet_piecemins_y; y <= tet_piecemaxs_y; y = y + 1)
945 for (x = tet_piecemins_x; x <= tet_piecemaxs_x; x = x + 1)
947 if (PieceMetric(x, y, rot, piece))
949 SetSquare(x + orgx, y + orgy, 0);
954 void CementPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
958 // why did I start counting from 1, damnit
962 pcolor = mod(piece, 3) + 1;
964 PieceMinsMaxs(rot, piece);
965 for (y = tet_piecemins_y; y <= tet_piecemaxs_y; y = y + 1)
967 for (x = tet_piecemins_x; x <= tet_piecemaxs_x; x = x + 1)
969 if (PieceMetric(x, y, rot, piece))
971 SetSquare(x + orgx, y + orgy, pcolor);
977 float LINE_LOW = 349525;
978 float LINE_HIGH = 699050; // above number times 2
980 void AddLines(float n)
985 FOR_EACH_REALCLIENT(head) if(head != self) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
986 head.tet_vs_addlines += n;
989 void CompletedLines()
991 float y, cleared, ln, added, pos, i;
998 if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)
999 cleared = cleared + 1;
1002 ln = GetLine(y - cleared);
1008 else if(cleared >= 1)
1009 AddLines(cleared - 1);
1011 self.tet_lines = self.tet_lines + cleared;
1012 TetAddScore(cleared * cleared * 10);
1014 added = self.tet_vs_addlines;
1015 self.tet_vs_addlines = 0;
1019 for(y = 1; y <= TET_LINES - added; ++y)
1021 SetLine(y, GetLine(y + added));
1023 for(y = max(1, TET_LINES - added + 1); y <= TET_LINES; ++y)
1025 pos = floor(random() * TET_WIDTH);
1027 for(i = 1; i <= TET_WIDTH; ++i)
1029 ln = SetXBlock(i, ln, floor(random() * 3 + 1));
1034 self.tet_highest_line = 0;
1035 for(y = 1; y <= TET_LINES; ++y)
1038 self.tet_highest_line = TET_LINES + 1 - y;
1044 else if(cleared >= 4)
1052 void HandleGame(float keyss)
1055 // first off, we need to see if we need a new piece
1064 if (self.piece_type == 0)
1066 self.piece_pos = TET_START_PIECE_POS; // that's about middle top, we count from 1 ARGH
1068 if(autocvar_g_bastet)
1070 self.piece_type = BastetPiece();
1071 self.next_piece = bastet_piece[6];
1075 if (self.next_piece)
1076 self.piece_type = self.next_piece;
1078 self.piece_type = RandomPiece();
1079 self.next_piece = RandomPiece();
1081 keyss = 0; // no movement first frame
1082 self.tet_autodown = time + 0.2;
1086 ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
1088 // next we need to check the piece metrics against what's on the level
1089 // based on the key order
1091 old_pos = check_pos = self.piece_pos;
1095 if (keyss & TETKEY_RIGHT)
1097 check_pos_x = check_pos_x + 1;
1100 else if (keyss & TETKEY_LEFT)
1102 check_pos_x = check_pos_x - 1;
1105 else if (keyss & TETKEY_ROTRIGHT)
1107 check_pos_z = check_pos_z + 1;
1108 piece_data = PieceShape(self.piece_type);
1112 else if (keyss & TETKEY_ROTLEFT)
1114 check_pos_z = check_pos_z - 1;
1115 piece_data = PieceShape(self.piece_type);
1120 if (check_pos_z > 3)
1122 else if (check_pos_z < 0)
1126 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
1127 self.piece_pos = check_pos;
1131 self.tet_gameovertime = time + 5;
1136 for(i = 1; i <= nudge; ++i)
1138 if(CheckMetrics(self.piece_type, check_pos_x + i, check_pos_y, check_pos_z))
1139 self.piece_pos = check_pos + '1 0 0' * i;
1140 else if(CheckMetrics(self.piece_type, check_pos_x - i, check_pos_y, check_pos_z))
1141 self.piece_pos = check_pos - '1 0 0' * i;
1147 check_pos = self.piece_pos;
1148 if(keyss & TETKEY_DROP)
1150 // drop to bottom, but do NOT cement it yet
1151 // this allows sliding it
1153 while(CheckMetrics(self.piece_type, check_pos_x, check_pos_y + 1, check_pos_z))
1155 self.tet_autodown = time + 2 / (1 + Tetris_Level());
1157 else if (keyss & TETKEY_DOWN)
1159 check_pos_y = check_pos_y + 1;
1160 self.tet_autodown = time + 2 / (1 + Tetris_Level());
1162 else if (self.tet_autodown < time)
1164 check_pos_y = check_pos_y + 1;
1165 self.tet_autodown = time + 2 / (1 + Tetris_Level());
1167 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
1169 if(old_pos != check_pos)
1170 self.tet_drawtime = 0;
1171 self.piece_pos = check_pos;
1175 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
1178 self.piece_type = 0;
1179 self.tet_drawtime = 0;
1182 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
1186 *********************************
1188 Important Linking Into Quake stuff
1190 *********************************
1194 void TetrisImpulse()
1196 if(self.tetris_on == 0 || self.tetris_on == 2) // from "off" or "game over"
1200 if(time < tet_vs_current_timeout)
1203 self.tet_vs_id = tet_vs_current_id;
1207 // start new VS game
1208 ++tet_vs_current_id;
1209 tet_vs_current_timeout = time + 15;
1210 self.tet_vs_id = tet_vs_current_id;
1211 bprint("^1TET^7R^1IS: ", self.netname, "^1 started a new game. Do ^7impulse 100^1 to join.\n");
1213 self.tet_highest_line = 0;
1215 self.tet_org = self.origin;
1216 self.movetype = MOVETYPE_NOCLIP;
1218 else if(self.tetris_on == 1) // from "on"
1226 float TetrisPreFrame()
1228 if (!self.tetris_on)
1231 self.tet_org = self.origin;
1232 if (self.tet_drawtime > time)
1235 if(self.tetris_on == 3)
1236 self.tet_drawtime = ceil(time - tet_vs_current_timeout + 0.1) + tet_vs_current_timeout;
1238 self.tet_drawtime = time + 0.5;
1241 float frik_anglemoda(float v)
1243 return v - floor(v/360) * 360;
1245 float angcompa(float y1, float y2)
1247 y1 = frik_anglemoda(y1);
1248 y2 = frik_anglemoda(y2);
1253 answer = answer - 360;
1254 else if (answer < -180)
1255 answer = answer + 360;
1259 .float tetkey_down, tetkey_rotright, tetkey_left, tetkey_right, tetkey_rotleft, tetkey_drop;
1261 float TetrisKeyRepeat(.float fld, float f)
1265 if(self.fld == 0) // initial key press
1267 self.fld = time + 0.3;
1270 else if(time > self.fld)
1272 self.fld = time + 0.1;
1277 // repeating too fast
1288 float TetrisPostFrame()
1294 if (!self.tetris_on)
1297 if(self.tetris_on == 2 && time > self.tet_gameovertime)
1303 if(self.tetris_on == 3 && time > tet_vs_current_timeout)
1305 self.tetris_on = 1; // start VS game
1306 self.tet_drawtime = 0;
1309 setorigin(self, self.tet_org);
1310 self.movetype = MOVETYPE_NONE;
1312 if(self.tetris_on == 1)
1314 if(TetrisKeyRepeat(tetkey_down, self.movement_x < 0))
1315 keysa |= TETKEY_DOWN;
1317 if(TetrisKeyRepeat(tetkey_rotright, self.movement_x > 0))
1318 keysa |= TETKEY_ROTRIGHT;
1320 if(TetrisKeyRepeat(tetkey_left, self.movement_y < 0))
1321 keysa |= TETKEY_LEFT;
1323 if(TetrisKeyRepeat(tetkey_right, self.movement_y > 0))
1324 keysa |= TETKEY_RIGHT;
1326 if(TetrisKeyRepeat(tetkey_rotleft, self.BUTTON_CROUCH))
1327 keysa |= TETKEY_ROTLEFT;
1329 if(TetrisKeyRepeat(tetkey_drop, self.BUTTON_JUMP))
1330 keysa |= TETKEY_DROP;