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