5 in cl_impulse.qc add TetrisImpulses(); to ImpulseCommands
7 in progs.src add g_tetris.qc after g_subs.qc
8 in cl_client.qc add if (TetrisPreFrame()) return; to PlayerPreThink
9 in cl_client.qc add if (TetrisPostFrame()) return; to PlayerPostThink
15 .float tetris_on, tet_time, tet_autodown;
17 .float piece_type, next_piece, tet_score, tet_lines;
19 var float tet_high_score = 0;
24 float TET_BORDER = 132;
25 float TET_BLOCKS = 132; // +1 = first color, +2, +3;
26 float TET_SPACE = 32; // blankness
31 float TETKEY_DOWN = 2;
32 float TETKEY_LEFT = 4;
33 float TETKEY_RIGHT = 8;
34 float TETKEY_ROTLEFT = 16;
35 float TETKEY_ROTRIGHT = 32;
39 .float line1, line2, line3, line4, line5, line6, line7,
40 line8, line9, line10, line11, line12, line13, line14, line15,
41 line16, line17, line18, line19, line20;
44 float SVC_CENTERPRINTa = 26;
48 *********************************
52 *********************************
54 void SetLine(float ln, float vl)
98 float GetLine(float ln)
144 float GetXBlock(float x, float dat)
149 return (dat & 12) / 4;
151 return (dat & 48) / 16;
153 return (dat & 192) / 64;
155 return (dat & 768) / 256;
157 return (dat & 3072) / 1024;
159 return (dat & 12288) / 4096;
161 return (dat & 49152) / 16384;
163 return (dat & 196608) / 65536;
165 return (dat & 786432) / 262144;
170 float SetXBlock(float x, float dat, float new)
173 return (dat - (dat & 3)) | new;
175 return (dat - (dat & 12)) | (new*4);
177 return (dat - (dat & 48)) | (new*16);
179 return (dat - (dat & 192)) | (new*64);
181 return (dat - (dat & 768)) | (new*256);
183 return (dat - (dat & 3072)) | (new*1024);
185 return (dat - (dat & 12288)) | (new*4096);
187 return (dat - (dat & 49152)) | (new*16384);
189 return (dat - (dat & 196608)) | (new*65536);
191 return (dat - (dat & 786432)) | (new*262144);
197 float GetSquare(float x, float y)
199 return GetXBlock(x, GetLine(y));
202 void SetSquare(float x, float y, float val)
207 dat = SetXBlock(x, dat, val & 3);
213 vector PieceShape(float pc)
222 return '20 20 0'; // 1 * 4 + 1 * 16
277 // do x 1..4 and y 1..4 in case of rotation
278 float PieceMetric(float x, float y, float rot, float pc)
283 // return bits of a piece
284 if (rot == 1) // 90 degrees
290 else if (rot == 2)//180
295 else if (rot == 3) // 270
301 if (x < 1 || y < 1 || x > 4 || y > 2)
303 piece_dat = PieceShape(pc);
305 return GetXBlock(x, piece_dat_x); // first row
307 return GetXBlock(x, piece_dat_y); // second row
309 return GetXBlock(x, piece_dat_z); // third row (doesn't exist)
311 return 0; // illegal parms
314 *********************************
318 *********************************
322 /* some prydon gate functions to make life easier....
324 somewhat modified because we don't need all the fanciness Prydon Gate is capable of
328 // remove warnings by adding underscores, i hate warnings, warnings suck
330 void p6(float _c1, float _c2, float _c3, float _c4, float c5, float c6)
332 WriteChar(MSG_ONE, _c1);
333 WriteChar(MSG_ONE, _c2);
334 WriteChar(MSG_ONE, _c3);
335 WriteChar(MSG_ONE, _c4);
336 WriteChar(MSG_ONE, c5);
337 WriteChar(MSG_ONE, c6);
340 float pnum(float num, float dig)
345 WriteChar(MSG_ONE, 45);
349 num = num - (f * 10);
351 dig = pnum(f, dig+1);
355 for (i = 0; i < (5 - dig); i = i + 1)
356 WriteChar(MSG_ONE, TET_SPACE);
358 WriteChar(MSG_ONE, 48 + num);
362 void DrawLine(float ln)
365 WriteChar(MSG_ONE, TET_BORDER);
367 for (x = 1; x <= TET_WIDTH; x = x + 1)
369 d = GetSquare(x, ln);
371 WriteChar(MSG_ONE, TET_BLOCKS + d);
373 WriteChar(MSG_ONE, TET_SPACE);
375 WriteChar(MSG_ONE, TET_BORDER);
378 void DrawPiece(float pc, float ln)
380 float x, d, piece_ln, pcolor;
383 if (pcolor == 0) // 4
385 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
387 piece_dat = PieceShape(pc);
389 piece_ln = piece_dat_x;
391 piece_ln = piece_dat_y;
392 for (x = 1; x <= 4; x = x + 1)
394 d = GetXBlock(x, piece_ln) * pcolor;
396 WriteChar(MSG_ONE, TET_BLOCKS + d);
398 WriteChar(MSG_ONE, TET_SPACE);
400 WriteChar(MSG_ONE, TET_SPACE); // pad to 6
406 WriteChar(MSG_ONE, SVC_CENTERPRINTa);
408 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
409 WriteChar(MSG_ONE, TET_BORDER);
410 p6(' ', ' ', ' ', ' ', ' ', ' ');
411 WriteChar(MSG_ONE, 10);
412 for (i = 1; i <= TET_LINES; i = i + 1)
416 p6(' ', 'N', 'E', 'X', 'T', ' ');
418 DrawPiece(self.next_piece, 1);
420 DrawPiece(self.next_piece, 2);
422 p6(' ', 'L', 'I', 'N', 'E', 'S');
424 pnum(self.tet_lines, 0);
426 p6(' ', 'S', 'C', 'O', 'R', 'E');
428 pnum(self.tet_score, 0);
430 p6(' ', 'H', 'I', 'G', 'H', ' ');
432 p6(' ', 'S', 'C', 'O', 'R', 'E');
434 pnum(tet_high_score, 0);
436 p6(' ', 'L', 'E', 'V', 'E', 'L');
438 pnum(floor(self.tet_lines / 20)+ 1, 0);
440 p6(' ', ' ', ' ', ' ', ' ', ' ');
441 WriteChar(MSG_ONE, 10);
445 for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
446 WriteChar(MSG_ONE, TET_BORDER);
447 p6(' ', ' ', ' ', ' ', ' ', ' ');
448 WriteChar(MSG_ONE, 10);
449 WriteChar(MSG_ONE, 0);
452 *********************************
456 *********************************
464 for (i=1; i<=TET_LINES; i = i + 1)
466 self.piece_pos = '0 0 0';
468 self.next_piece = self.tet_lines = self.tet_score = 0;
474 centerprint(self, "Game Over");
477 self.movetype = MOVETYPE_WALK;
483 *********************************
487 *********************************
491 return floor(random() * PIECES) + 1;
494 void TetAddScore(float n)
496 self.tet_score = self.tet_score + n;
497 if (self.tet_score > tet_high_score)
498 tet_high_score = self.tet_score;
500 float CheckMetrics(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
502 // check to see if the piece, if moved to the locations will overlap
505 // why did I start counting from 1, damnit
509 for (y = 1; y < 5; y = y + 1)
511 for (x = 1; x < 5; x = x + 1)
513 if (PieceMetric(x, y, rot, piece))
515 if (GetSquare(x + orgx, y + orgy))
516 return FALSE; // uhoh, gonna hit something.
517 if (x+orgx<1 || x+orgx > TET_WIDTH || y+orgy<1 || y+orgy> TET_LINES)
518 return FALSE; // ouside the level
525 void ClearPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
529 // why did I start counting from 1, damnit
533 for (y = 1; y < 5; y = y + 1)
535 for (x = 1; x < 5; x = x + 1)
537 if (PieceMetric(x, y, rot, piece))
539 SetSquare(x + orgx, y + orgy, 0);
544 void CementPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
548 // why did I start counting from 1, damnit
553 if (pcolor == 0) // 4
556 for (y = 1; y < 5; y = y + 1)
558 for (x = 1; x < 5; x = x + 1)
560 if (PieceMetric(x, y, rot, piece))
562 SetSquare(x + orgx, y + orgy, pcolor);
568 float LINE_LOW = 349525;
569 float LINE_HIGH = 699050; // above number times 2
571 void CompletedLines()
573 float y, cleared, ln;
580 if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)
581 cleared = cleared + 1;
584 ln = GetLine(y - cleared);
587 self.tet_lines = self.tet_lines + cleared;
588 TetAddScore(cleared * cleared * 10);
591 void HandleGame(float keyss)
594 // first off, we need to see if we need a new piece
598 if (self.tet_time > time)
600 self.tet_time = time + 0.1;
603 if (self.piece_type == 0)
605 self.piece_pos = '5 1 0'; // that's about middle top, we count from 1 ARGH
607 self.piece_type = self.next_piece;
609 self.piece_type = RandomPiece();
610 self.next_piece = RandomPiece();
611 keyss = 0; // no movement first frame
612 self.tet_autodown = time + 0.2;
616 ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
618 // next we need to check the piece metrics against what's on the level
619 // based on the key order
621 check_pos = self.piece_pos;
623 if (keyss & TETKEY_RIGHT)
624 check_pos_x = check_pos_x + 1;
625 else if (keyss & TETKEY_LEFT)
626 check_pos_x = check_pos_x - 1;
627 else if (keyss & TETKEY_ROTRIGHT)
628 check_pos_z = check_pos_z + 1;
629 else if (keyss & TETKEY_ROTLEFT)
630 check_pos_z = check_pos_z - 1;
634 else if (check_pos_z < 0)
638 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
639 self.piece_pos = check_pos;
642 check_pos = self.piece_pos;
643 if (keyss & TETKEY_DOWN)
644 check_pos_y = check_pos_y + 1;
645 else if (self.tet_autodown < time)
647 check_pos_y = check_pos_y + 1;
648 self.tet_autodown = time + 1 / (floor(self.tet_lines / 20) + 1);
650 if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
651 self.piece_pos = check_pos;
654 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
660 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
664 *********************************
666 Important Linking Into Quake stuff
668 *********************************
672 void TetrisImpulses(float imp)
678 self.tet_org = self.origin;
679 self.movetype = MOVETYPE_NOCLIP;
680 stuffcmd(self, "cl_bob 0\ncl_rollangle 0\n");
685 float TetrisPreFrame()
690 self.tet_org = self.origin;
691 if (self.tet_time > time)
697 float frik_anglemoda(float v)
699 return v - floor(v/360) * 360;
701 float angcompa(float y1, float y2)
703 y1 = frik_anglemoda(y1);
704 y2 = frik_anglemoda(y2);
709 answer = answer - 360;
710 else if (answer < -180)
711 answer = answer + 360;
716 float TetrisPostFrame()
726 if (self.origin != self.tet_org)
728 self.origin = self.tet_org;
729 if(self.movement_x < 0)
730 keysa |= TETKEY_DOWN;
731 else if(self.movement_x > 0)
733 if(self.movement_y < 0)
734 keysa |= TETKEY_LEFT;
735 else if(self.movement_y > 0)
736 keysa |= TETKEY_RIGHT;
738 if (self.BUTTON_ATCK)
739 keysa = keysa | TETKEY_ROTRIGHT;
740 if (self.BUTTON_JUMP)
741 keysa = keysa | TETKEY_ROTLEFT;