5 in weapons.qc add TetrisImpulses(); to ImpulseCommands
\r
7 in progs.src add tetris.qc after subs.qc
\r
8 in client.qc add if (TetrisPreFrame()) return; to PlayerPreThink
\r
9 in client.qc add if (TetrisPostFrame()) return; to PlayerPostThink
\r
15 .float tetris_on, tet_time, tet_autodown;
\r
17 .float piece_type, next_piece, tet_score, tet_lines;
\r
19 var float tet_high_score = 0;
\r
21 float TET_LINES = 20;
\r
22 float TET_WIDTH = 10;
\r
24 float TET_BORDER = 132;
\r
25 float TET_BLOCKS = 132; // +1 = first color, +2, +3;
\r
26 float TET_SPACE = 32; // blankness
\r
30 float TETKEY_UP = 1;
\r
31 float TETKEY_DOWN = 2;
\r
32 float TETKEY_LEFT = 4;
\r
33 float TETKEY_RIGHT = 8;
\r
34 float TETKEY_ROTLEFT = 16;
\r
35 float TETKEY_ROTRIGHT = 32;
\r
39 .float line1, line2, line3, line4, line5, line6, line7,
\r
40 line8, line9, line10, line11, line12, line13, line14, line15,
\r
41 line16, line17, line18, line19, line20;
\r
44 float SVC_CENTERPRINTa = 26;
\r
48 *********************************
\r
52 *********************************
\r
54 void (float ln, float vl) SetLine =
\r
98 float (float ln) GetLine =
\r
119 return self.line10;
\r
121 return self.line11;
\r
123 return self.line12;
\r
125 return self.line13;
\r
127 return self.line14;
\r
129 return self.line15;
\r
131 return self.line16;
\r
133 return self.line17;
\r
135 return self.line18;
\r
137 return self.line19;
\r
139 return self.line20;
\r
144 float(float x, float dat) GetXBlock =
\r
149 return (dat & 12) / 4;
\r
151 return (dat & 48) / 16;
\r
153 return (dat & 192) / 64;
\r
155 return (dat & 768) / 256;
\r
157 return (dat & 3072) / 1024;
\r
159 return (dat & 12288) / 4096;
\r
161 return (dat & 49152) / 16384;
\r
163 return (dat & 196608) / 65536;
\r
165 return (dat & 786432) / 262144;
\r
170 float(float x, float dat, float new) SetXBlock =
\r
173 return (dat - (dat & 3)) | new;
\r
175 return (dat - (dat & 12)) | (new*4);
\r
177 return (dat - (dat & 48)) | (new*16);
\r
179 return (dat - (dat & 192)) | (new*64);
\r
181 return (dat - (dat & 768)) | (new*256);
\r
183 return (dat - (dat & 3072)) | (new*1024);
\r
185 return (dat - (dat & 12288)) | (new*4096);
\r
187 return (dat - (dat & 49152)) | (new*16384);
\r
189 return (dat - (dat & 196608)) | (new*65536);
\r
191 return (dat - (dat & 786432)) | (new*262144);
\r
197 float(float x, float y) GetSquare =
\r
199 return GetXBlock(x, GetLine(y));
\r
202 void (float x, float y, float val) SetSquare =
\r
207 dat = SetXBlock(x, dat, val & 3);
\r
213 vector(float pc) PieceShape =
\r
222 return '20 20 0'; // 1 * 4 + 1 * 16
\r
277 // do x 1..4 and y 1..4 in case of rotation
\r
278 float(float x, float y, float rot, float pc) PieceMetric =
\r
283 // return bits of a piece
\r
284 if (rot == 1) // 90 degrees
\r
290 else if (rot == 2)//180
\r
295 else if (rot == 3) // 270
\r
301 if (x < 1 || y < 1 || x > 4 || y > 2)
\r
303 piece_dat = PieceShape(pc);
\r
305 return GetXBlock(x, piece_dat_x); // first row
\r
307 return GetXBlock(x, piece_dat_y); // second row
\r
309 return GetXBlock(x, piece_dat_z); // third row (doesn't exist)
\r
311 return 0; // illegal parms
\r
314 *********************************
\r
318 *********************************
\r
322 /* some prydon gate functions to make life easier....
\r
324 somewhat modified because we don't need all the fanciness Prydon Gate is capable of
\r
328 void(float c1, float c2, float c3, float c4, float c5, float c6) p6 =
\r
330 WriteChar(MSG_ONE, c1);
\r
331 WriteChar(MSG_ONE, c2);
\r
332 WriteChar(MSG_ONE, c3);
\r
333 WriteChar(MSG_ONE, c4);
\r
334 WriteChar(MSG_ONE, c5);
\r
335 WriteChar(MSG_ONE, c6);
\r
338 float(float num, float dig) pnum =
\r
343 WriteChar(MSG_ONE, 45);
\r
346 f = floor(num / 10);
\r
347 num = num - (f * 10);
\r
349 dig = pnum(f, dig+1);
\r
353 for (i = 0; i < (5 - dig); i = i + 1)
\r
354 WriteChar(MSG_ONE, TET_SPACE);
\r
356 WriteChar(MSG_ONE, 48 + num);
\r
360 void (float ln) DrawLine =
\r
363 WriteChar(MSG_ONE, TET_BORDER);
\r
365 for (x = 1; x <= TET_WIDTH; x = x + 1)
\r
367 d = GetSquare(x, ln);
\r
369 WriteChar(MSG_ONE, TET_BLOCKS + d);
\r
371 WriteChar(MSG_ONE, TET_SPACE);
\r
373 WriteChar(MSG_ONE, TET_BORDER);
\r
376 void (float pc, float ln) DrawPiece =
\r
378 float x, d, piece_ln, color;
\r
381 if (color == 0) // 4
\r
383 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
\r
385 piece_dat = PieceShape(pc);
\r
387 piece_ln = piece_dat_x;
\r
389 piece_ln = piece_dat_y;
\r
390 for (x = 1; x <= 4; x = x + 1)
\r
392 d = GetXBlock(x, piece_ln) * color;
\r
394 WriteChar(MSG_ONE, TET_BLOCKS + d);
\r
396 WriteChar(MSG_ONE, TET_SPACE);
\r
398 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
\r
400 void() Draw_Tetris =
\r
404 WriteChar(MSG_ONE, SVC_CENTERPRINTa);
\r
406 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
\r
407 WriteChar(MSG_ONE, TET_BORDER);
\r
408 p6(' ', ' ', ' ', ' ', ' ', ' ');
\r
409 WriteChar(MSG_ONE, 10);
\r
410 for (i = 1; i <= TET_LINES; i = i + 1)
\r
414 p6(' ', 'N', 'E', 'X', 'T', ' ');
\r
416 DrawPiece(self.next_piece, 1);
\r
418 DrawPiece(self.next_piece, 2);
\r
420 p6(' ', 'L', 'I', 'N', 'E', 'S');
\r
422 pnum(self.tet_lines, 0);
\r
424 p6(' ', 'S', 'C', 'O', 'R', 'E');
\r
426 pnum(self.tet_score, 0);
\r
428 p6(' ', 'H', 'I', 'G', 'H', ' ');
\r
430 p6(' ', 'S', 'C', 'O', 'R', 'E');
\r
432 pnum(tet_high_score, 0);
\r
434 p6(' ', 'L', 'E', 'V', 'E', 'L');
\r
436 pnum(floor(self.tet_lines / 20)+ 1, 0);
\r
438 p6(' ', ' ', ' ', ' ', ' ', ' ');
\r
439 WriteChar(MSG_ONE, 10);
\r
443 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
\r
444 WriteChar(MSG_ONE, TET_BORDER);
\r
445 p6(' ', ' ', ' ', ' ', ' ', ' ');
\r
446 WriteChar(MSG_ONE, 10);
\r
447 WriteChar(MSG_ONE, 0);
\r
450 *********************************
\r
454 *********************************
\r
458 void() ResetTetris =
\r
462 for (i=1; i<=TET_LINES; i = i + 1)
\r
464 self.piece_pos = '0 0 0';
\r
465 self.piece_type = 0;
\r
466 self.next_piece = self.tet_lines = self.tet_score = 0;
\r
470 void () Tet_GameOver =
\r
472 centerprint(self, "Game Over");
\r
473 self.tetris_on = 0;
\r
475 self.movetype = MOVETYPE_WALK;
\r
481 *********************************
\r
485 *********************************
\r
487 float() RandomPiece =
\r
489 return floor(random() * PIECES) + 1;
\r
492 void(float n) TetAddScore =
\r
494 self.tet_score = self.tet_score + n;
\r
495 if (self.tet_score > tet_high_score)
\r
496 tet_high_score = self.tet_score;
\r
498 float CheckMetrics(float piece, float orgx, float orgy, float rot) =
\r
500 // check to see if the piece, if moved to the locations will overlap
\r
503 // why did I start counting from 1, damnit
\r
507 for (y = 1; y < 5; y = y + 1)
\r
509 for (x = 1; x < 5; x = x + 1)
\r
511 if (PieceMetric(x, y, rot, piece))
\r
513 if (GetSquare(x + orgx, y + orgy))
\r
514 return FALSE; // uhoh, gonna hit something.
\r
515 if (x+orgx<1 || x+orgx > TET_WIDTH || y+orgy<1 || y+orgy> TET_LINES)
\r
516 return FALSE; // ouside the level
\r
523 void ClearPiece(float piece, float orgx, float orgy, float rot) =
\r
527 // why did I start counting from 1, damnit
\r
531 for (y = 1; y < 5; y = y + 1)
\r
533 for (x = 1; x < 5; x = x + 1)
\r
535 if (PieceMetric(x, y, rot, piece))
\r
537 SetSquare(x + orgx, y + orgy, 0);
\r
542 void CementPiece(float piece, float orgx, float orgy, float rot) =
\r
546 // why did I start counting from 1, damnit
\r
551 if (color == 0) // 4
\r
554 for (y = 1; y < 5; y = y + 1)
\r
556 for (x = 1; x < 5; x = x + 1)
\r
558 if (PieceMetric(x, y, rot, piece))
\r
560 SetSquare(x + orgx, y + orgy, color);
\r
566 float LINE_LOW = 349525;
\r
567 float LINE_HIGH = 699050; // above number times 2
\r
569 void() CompletedLines =
\r
571 float y, cleared, ln;
\r
578 if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)
\r
579 cleared = cleared + 1;
\r
582 ln = GetLine(y - cleared);
\r
585 self.tet_lines = self.tet_lines + cleared;
\r
586 TetAddScore(cleared * cleared * 10);
\r
589 void(float keyss) HandleGame =
\r
592 // first off, we need to see if we need a new piece
\r
596 if (self.tet_time > time)
\r
598 self.tet_time = time + 0.1;
\r
601 if (self.piece_type == 0)
\r
603 self.piece_pos = '5 1 0'; // that's about middle top, we count from 1 ARGH
\r
604 if (self.next_piece)
\r
605 self.piece_type = self.next_piece;
\r
607 self.piece_type = RandomPiece();
\r
608 self.next_piece = RandomPiece();
\r
609 keyss = 0; // no movement first frame
\r
610 self.tet_autodown = time + 0.2;
\r
614 ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
\r
616 // next we need to check the piece metrics against what's on the level
\r
617 // based on the key order
\r
619 check_pos = self.piece_pos;
\r
621 if (keyss & TETKEY_RIGHT)
\r
622 check_pos_x = check_pos_x + 1;
\r
623 else if (keyss & TETKEY_LEFT)
\r
624 check_pos_x = check_pos_x - 1;
\r
625 else if (keyss & TETKEY_ROTRIGHT)
\r
626 check_pos_z = check_pos_z + 1;
\r
627 else if (keyss & TETKEY_ROTLEFT)
\r
628 check_pos_z = check_pos_z - 1;
\r
630 if (check_pos_z > 3)
\r
632 else if (check_pos_z < 0)
\r
636 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
\r
637 self.piece_pos = check_pos;
\r
638 else if (brand_new)
\r
640 check_pos = self.piece_pos;
\r
641 if (keyss & TETKEY_DOWN)
\r
642 check_pos_y = check_pos_y + 1;
\r
643 else if (self.tet_autodown < time)
\r
645 check_pos_y = check_pos_y + 1;
\r
646 self.tet_autodown = time + 1 / (floor(self.tet_lines / 20) + 1);
\r
648 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
\r
649 self.piece_pos = check_pos;
\r
652 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
\r
655 self.piece_type = 0;
\r
658 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
\r
662 *********************************
\r
664 Important Linking Into Quake stuff
\r
666 *********************************
\r
670 void() TetrisImpulses =
\r
672 if (self.impulse == 200)
\r
674 self.tetris_on = 1;
\r
676 self.tet_org = self.origin;
\r
677 self.movetype = MOVETYPE_NOCLIP;
\r
678 stuffcmd(self, "cl_bob 0\ncl_rollangle 0\n");
\r
683 float() TetrisPreFrame =
\r
685 if (!self.tetris_on)
\r
688 self.tet_org = self.origin;
\r
689 if (self.tet_time > time)
\r
695 float(float v) frik_anglemoda =
\r
697 return v - floor(v/360) * 360;
\r
699 float (float y1, float y2) angcompa =
\r
701 y1 = frik_anglemoda(y1);
\r
702 y2 = frik_anglemoda(y2);
\r
704 local float answer;
\r
707 answer = answer - 360;
\r
708 else if (answer < -180)
\r
709 answer = answer + 360;
\r
714 float() TetrisPostFrame =
\r
721 if (!self.tetris_on)
\r
723 if (self.origin != self.tet_org)
\r
725 mov = vectoangles(self.origin - self.tet_org);
\r
726 self.origin = self.tet_org;
\r
728 norm = angcompa(self.v_angle_y, mov_y);
\r
730 if (norm > -80 && norm < 80)
\r
731 keysa = keysa | TETKEY_UP;
\r
732 if (norm > 10 && norm < 170)
\r
733 keysa = keysa | TETKEY_RIGHT;
\r
734 if (norm > 100 || norm < -260)
\r
735 keysa = keysa | TETKEY_DOWN;
\r
736 if (norm > -170 && norm < -10)
\r
737 keysa = keysa | TETKEY_LEFT;
\r
740 keysa = keysa | TETKEY_ROTRIGHT;
\r
742 keysa = keysa | TETKEY_ROTLEFT;
\r