]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_tetris.qc
tetris: SRS rotation
[divverent/nexuiz.git] / data / qcsrc / server / g_tetris.qc
1 /*
2
3 Installation:
4
5 compile with -DTETRIS
6
7 */
8
9 #ifdef TETRIS
10
11 .vector tet_org;
12
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;
18 .vector piece_pos;
19 .float piece_type, next_piece, tet_score, tet_lines;
20
21 // tetris_on states:
22 //   1 = running
23 //   2 = game over
24 //   3 = waiting for VS players
25
26 var float tet_high_score = 0;
27
28 float TET_LINES = 20;
29 float TET_WIDTH = 10;
30 //character values
31 float TET_BORDER = 139;
32 float TET_BLOCK = 133;
33 float TET_SPACE = 160; // blankness
34
35
36
37 float TETKEY_UP = 1;
38 float TETKEY_DOWN = 2;
39 float TETKEY_LEFT = 4;
40 float TETKEY_RIGHT = 8;
41 float TETKEY_ROTLEFT = 16;
42 float TETKEY_ROTRIGHT = 32;
43 float TETKEY_DROP = 64;
44 string TET_PADDING_RIGHT = "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"; // get away from crosshair
45
46 float PIECES = 7;
47
48 .float line1, line2, line3, line4, line5, line6, line7,
49 line8, line9, line10, line11, line12, line13, line14, line15,
50 line16, line17, line18, line19, line20;
51
52
53 float   SVC_CENTERPRINTa                = 26;
54
55 float Tetris_Level()
56
57         return ((floor((self.tet_lines / 10)) + 1)); 
58 }; 
59
60 void tetsnd(string snd)
61 {
62         play2(self, strcat("sounds/tetris/", snd));
63 }
64
65 /*
66 *********************************
67
68 Library Functions
69
70 *********************************
71 */
72 void SetLine(float ln, float vl)
73 {
74         if (ln == 1)
75                 self.line1 = vl;
76         else if (ln == 2)
77                 self.line2 = vl;
78         else if (ln == 3)
79                 self.line3 = vl;
80         else if (ln == 4)
81                 self.line4 = vl;
82         else if (ln == 5)
83                 self.line5 = vl;
84         else if (ln == 6)
85                 self.line6 = vl;
86         else if (ln == 7)
87                 self.line7 = vl;
88         else if (ln == 8)
89                 self.line8 = vl;
90         else if (ln == 9)
91                 self.line9 = vl;
92         else if (ln == 10)
93                 self.line10 = vl;
94         else if (ln == 11)
95                 self.line11 = vl;
96         else if (ln == 12)
97                 self.line12 = vl;
98         else if (ln == 13)
99                 self.line13 = vl;
100         else if (ln == 14)
101                 self.line14 = vl;
102         else if (ln == 15)
103                 self.line15 = vl;
104         else if (ln == 16)
105                 self.line16 = vl;
106         else if (ln == 17)
107                 self.line17 = vl;
108         else if (ln == 18)
109                 self.line18 = vl;
110         else if (ln == 19)
111                 self.line19 = vl;
112         else if (ln == 20)
113                 self.line20 = vl;
114 };
115
116 float GetLine(float ln)
117 {
118         if (ln == 1)
119                 return self.line1;
120         else if (ln == 2)
121                 return self.line2;
122         else if (ln == 3)
123                 return self.line3;
124         else if (ln == 4)
125                 return self.line4;
126         else if (ln == 5)
127                 return self.line5;
128         else if (ln == 6)
129                 return self.line6;
130         else if (ln == 7)
131                 return self.line7;
132         else if (ln == 8)
133                 return self.line8;
134         else if (ln == 9)
135                 return self.line9;
136         else if (ln == 10)
137                 return self.line10;
138         else if (ln == 11)
139                 return self.line11;
140         else if (ln == 12)
141                 return self.line12;
142         else if (ln == 13)
143                 return self.line13;
144         else if (ln == 14)
145                 return self.line14;
146         else if (ln == 15)
147                 return self.line15;
148         else if (ln == 16)
149                 return self.line16;
150         else if (ln == 17)
151                 return self.line17;
152         else if (ln == 18)
153                 return self.line18;
154         else if (ln == 19)
155                 return self.line19;
156         else if (ln == 20)
157                 return self.line20;
158         else
159                 return 0;
160 };
161
162 float GetXBlock(float x, float dat)
163 {
164         if (x == 1)
165                 return dat & 3;
166         else if (x == 2)
167                 return (dat & 12) / 4;
168         else if (x == 3)
169                 return (dat & 48) / 16;
170         else if (x == 4)
171                 return (dat & 192) / 64;
172         else if (x == 5)
173                 return (dat & 768) / 256;
174         else if (x == 6)
175                 return (dat & 3072) / 1024;
176         else if (x == 7)
177                 return (dat & 12288) / 4096;
178         else if (x == 8)
179                 return (dat & 49152) / 16384;
180         else if (x == 9)
181                 return (dat & 196608) / 65536;
182         else if (x == 10)
183                 return (dat & 786432) / 262144;
184         else
185                 return 0;
186 };
187
188 float SetXBlock(float x, float dat, float new)
189 {
190         if (x == 1)
191                 return (dat - (dat & 3)) | new;
192         else if (x == 2)
193                 return (dat - (dat & 12)) | (new*4);
194         else if (x == 3)
195                 return (dat - (dat & 48)) | (new*16);
196         else if (x == 4)
197                 return (dat - (dat & 192)) | (new*64);
198         else if (x == 5)
199                 return (dat - (dat & 768)) | (new*256);
200         else if (x == 6)
201                 return (dat - (dat & 3072)) | (new*1024);
202         else if (x == 7)
203                 return (dat - (dat & 12288)) | (new*4096);
204         else if (x == 8)
205                 return (dat - (dat & 49152)) | (new*16384);
206         else if (x == 9)
207                 return (dat - (dat & 196608)) | (new*65536);
208         else if (x == 10)
209                 return (dat - (dat & 786432)) | (new*262144);
210         else
211                 return dat;
212 };
213
214
215 float GetSquare(float x, float y)
216 {
217         return GetXBlock(x,  GetLine(y));
218 };
219
220 void SetSquare(float x, float y, float val)
221 {
222         float dat;
223
224         dat = GetLine(y);
225         dat  = SetXBlock(x, dat, val & 3);
226         SetLine(y, dat);
227 };
228
229 vector PieceShape(float pc)
230 {
231         if (pc == 1)
232                 return '5 5 0'; // O
233         else if (pc == 2)
234                 return '85 0 0'; // I
235         else if (pc == 3)
236                 return '21 1 0'; // L
237         else if (pc == 4)
238                 return '1 21 0'; // J
239         else if (pc == 5)
240                 return '5 20 0'; // Z
241         else if (pc == 6)
242                 return '20 5 0'; // S
243         else if (pc == 7)
244                 return '4 21 0'; // T
245         else
246                 return '0 0 0';
247 }
248
249 vector PieceCenter(float pc)
250 {
251         if(pc == 1)
252                 return '1.5 1.5 0'; // O
253         else if (pc == 2)
254                 return '2.5 1.5 0'; // I
255         else if (pc == 3)
256                 return '2 1 0'; // L
257         else if (pc == 4)
258                 return '2 2 0'; // J
259         else if (pc == 5)
260                 return '2 2 0'; // Z
261         else if (pc == 6)
262                 return '2 2 0'; // S
263         else if (pc == 7)
264                 return '2 2 0'; // T
265         else
266                 return '0 0 0';
267 }
268
269 // do x 1..4 and y 1..4 in case of rotation
270 float PieceMetric(float x, float y, float rot, float pc)
271 {
272         float t;
273         vector piece_dat;
274         float wid;
275
276         // return bits of a piece
277         wid = piece_dat_z + 1;
278         piece_dat = PieceCenter(pc);
279         if (rot == 1) // 90 degrees
280         {
281                 // x+cx, y+cy -> -y+cx, x+cy
282                 // X, Y       -> -Y+cy+cx, X-cx+cy
283                 //   x = X-cx
284                 //   y = Y-cy
285                 t = y;
286                 y = x - piece_dat_x + piece_dat_y;
287                 x = -t + piece_dat_x + piece_dat_y;
288         }
289         else if (rot == 2)//180
290         {
291                 x = 2 * piece_dat_x - x;
292                 y = 2 * piece_dat_y - y;
293         }
294         else if (rot == 3) // 270
295         {
296                 // x+cx, y+cy -> y+cx, -x+cy
297                 // X, Y       -> Y-cy+cx, -X+cx+cy
298                 //   x = X-cx
299                 //   y = Y-cy
300                 t = y;
301                 y = -x + piece_dat_y + piece_dat_x;
302                 x =  t - piece_dat_y + piece_dat_x;
303         }
304         if (x < 1 || y < 1 || x > 4 || y > 2)
305                 return 0;
306         piece_dat = PieceShape(pc);
307         if (y == 1)
308                 return GetXBlock(x, piece_dat_x); // first row
309         else if (y == 2)
310                 return GetXBlock(x, piece_dat_y); // second row
311         else
312                 return 0; // illegal parms
313 };
314 /*
315 *********************************
316
317 Draw
318
319 *********************************
320 */
321
322
323 /* some prydon gate functions to make life easier....
324
325 somewhat modified because we don't need all the fanciness Prydon Gate is capable of
326
327 */
328
329 void WriteTetrisString(string s)
330 {
331         WriteUnterminatedString(MSG_ONE, strconv(0, 2, 2, s));
332 }
333
334 float pnum(float num, float dig)
335 {
336         local float f, i;
337         if (num < 0)
338         {
339                 WriteChar(MSG_ONE, 173);
340                 num = 0 - num;
341         }
342         f = floor(num / 10);
343         num = num - (f * 10);
344         if (f)
345                 dig = pnum(f, dig+1);
346         else
347         {
348                 // pad to 6
349                 for (i = 0; i < (5 - dig); i = i + 1)
350                         WriteChar(MSG_ONE, TET_SPACE);
351         }
352         WriteChar(MSG_ONE, 176 + num);
353         return dig;
354 };
355
356 void DrawLine(float ln)
357 {
358         float x, d;
359         WriteChar(MSG_ONE, TET_BORDER);
360
361         for (x = 1; x <= TET_WIDTH; x = x + 1)
362         {
363                 d = GetSquare(x, ln);
364                 if (d)
365                 {
366                         WriteChar(MSG_ONE, '^');
367                         WriteChar(MSG_ONE, d * d - 2 * d + 50); // 1, 2, 5
368                         WriteChar(MSG_ONE, TET_BLOCK);
369                 }
370                 else
371                         WriteChar(MSG_ONE, TET_SPACE);
372         }
373         WriteChar(MSG_ONE, '^');
374         WriteChar(MSG_ONE, '7');
375         WriteChar(MSG_ONE, TET_BORDER);
376 }
377
378 void DrawPiece(float pc, float ln)
379 {
380         float x, d, piece_ln, pcolor;
381         vector piece_dat;
382         pcolor = mod(pc, 3) + 1;
383         WriteChar(MSG_ONE, TET_SPACE); // pad to 6
384
385         piece_dat = PieceShape(pc);
386         if (ln == 1)
387                 piece_ln = piece_dat_x;
388         else
389                 piece_ln = piece_dat_y;
390         for (x = 1; x <= 4; x = x + 1)
391         {
392                 d = GetXBlock(x, piece_ln) * pcolor;
393                 if (d)
394                 {
395                         WriteChar(MSG_ONE, '^');
396                         WriteChar(MSG_ONE, d * d - 2 * d + 50); // 1, 2, 5
397                         WriteChar(MSG_ONE, TET_BLOCK);
398                 }
399                 else
400                         WriteChar(MSG_ONE, TET_SPACE);
401         }
402         WriteChar(MSG_ONE, TET_SPACE);  // pad to 6
403 }
404 void Draw_Tetris()
405 {
406         float i;
407         entity head;
408         msg_entity = self;
409         WriteChar(MSG_ONE, SVC_CENTERPRINTa);
410         // decoration
411         for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
412                 WriteChar(MSG_ONE, TET_BORDER);
413         WriteTetrisString("      ");
414         WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
415         WriteChar(MSG_ONE, 10);
416         for (i = 1; i <= TET_LINES; i = i + 1)
417         {
418                 if(self.tetris_on == 2)
419                         WriteTetrisString(" GAME  OVER ");
420                 else if(self.tetris_on == 3)
421                         WriteTetrisString("PLEASE  WAIT");
422                 else
423                         DrawLine(i);
424                 if (i == 1)
425                         WriteTetrisString(" NEXT ");
426                 else if (i == 3)
427                         DrawPiece(self.next_piece, 1);
428                 else if (i == 4)
429                         DrawPiece(self.next_piece, 2);
430                 else if (i == 6)
431                         WriteTetrisString(" LINES");
432                 else if (i == 7)
433                         pnum(self.tet_lines, 0);
434                 else if (i == 9)
435                         WriteTetrisString(" SCORE");
436                 else if (i == 10)
437                         pnum(self.tet_score, 0);
438                 else if (i == 12)
439                         WriteTetrisString(" HIGH ");
440                 else if (i == 13)
441                         WriteTetrisString(" SCORE");
442                 else if (i == 14)
443                         pnum(tet_high_score, 0);
444                 else if (i == 16)
445                         WriteTetrisString(" LEVEL");
446                 else if (i == 17)
447                         pnum(Tetris_Level(), 0);
448                 else
449                         WriteTetrisString("      ");
450                 WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
451                 WriteChar(MSG_ONE, 10);
452         }
453         // decoration
454
455         for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
456                 WriteChar(MSG_ONE, TET_BORDER);
457         WriteTetrisString("      ");
458         WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
459         WriteChar(MSG_ONE, 10);
460
461         // VS game status
462         if(self.tet_vs_id)
463         {
464                 WriteChar(MSG_ONE, 10);
465                 WriteChar(MSG_ONE, 10);
466                 if(self.tetris_on == 3)
467                 {
468                         WriteUnterminatedString(MSG_ONE, strcat("WAITING FOR OTHERS (", ftos(ceil(tet_vs_current_timeout - time)), " SEC)\n"));
469                 }
470
471                 WriteChar(MSG_ONE, 10);
472                 FOR_EACH_REALCLIENT(head) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
473                 {
474                         if(head == self)
475                                 WriteUnterminatedString(MSG_ONE, ">");
476                         else
477                                 WriteUnterminatedString(MSG_ONE, " ");
478                         if(head.tetris_on == 2)
479                                 WriteUnterminatedString(MSG_ONE, "   X_X");
480                         else
481                                 pnum(head.tet_highest_line, 0);
482                         WriteUnterminatedString(MSG_ONE, " ");
483                         WriteUnterminatedString(MSG_ONE, head.netname);
484                         WriteChar(MSG_ONE, 10);
485                 }
486         }
487
488         WriteChar(MSG_ONE, 0);
489 }
490 /*
491 *********************************
492
493 Game Functions
494
495 *********************************
496 */
497
498 // reset the game
499 void ResetTetris()
500 {
501         float i;
502
503         for (i=1; i<=TET_LINES; i = i + 1)
504                 SetLine(i, 0);
505         self.piece_pos = '0 0 0';
506         self.piece_type = 0;
507         self.next_piece = self.tet_lines = self.tet_score = 0;
508
509 };
510
511 void Tet_GameExit()
512 {
513         centerprint(self, " ");
514         self.tetris_on = 0;
515         self.tet_vs_id = 0;
516         ResetTetris();
517         self.movetype = MOVETYPE_WALK;
518 };
519
520
521
522 /*
523 *********************************
524
525 Game Mechanics
526
527 *********************************
528 */
529 float RandomPiece()
530 {
531         return floor(random() * PIECES) + 1;
532 };
533
534 void TetAddScore(float n)
535 {
536         self.tet_score = self.tet_score + n * Tetris_Level();
537         if (self.tet_score > tet_high_score)
538                 tet_high_score = self.tet_score;
539 };
540 float CheckMetrics(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
541 {
542         // check to see if the piece, if moved to the locations will overlap
543
544         float x, y;
545         // why did I start counting from 1, damnit
546         orgx = orgx - 1;
547         orgy = orgy - 1;
548
549         for (y = 0; y < 5; y = y + 1)
550         {
551                 for (x = 0; x < 5; x = x + 1)
552                 {
553                         if (PieceMetric(x, y, rot, piece))
554                         {
555                                 if (GetSquare(x + orgx, y + orgy))
556                                         return FALSE; // uhoh, gonna hit something.
557                                 if (x+orgx<1 || x+orgx > TET_WIDTH || y+orgy<1 || y+orgy> TET_LINES)
558                                         return FALSE; // ouside the level
559                         }
560                 }
561         }
562         return TRUE;
563 }
564
565 void ClearPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
566 {
567
568         float x, y;
569         // why did I start counting from 1, damnit
570         orgx = orgx - 1;
571         orgy = orgy - 1;
572
573         for (y = 0; y < 5; y = y + 1)
574         {
575                 for (x = 0; x < 5; x = x + 1)
576                 {
577                         if (PieceMetric(x, y, rot, piece))
578                         {
579                                 SetSquare(x + orgx, y + orgy, 0);
580                         }
581                 }
582         }
583 }
584 void CementPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
585 {
586         float pcolor;
587         float x, y;
588         // why did I start counting from 1, damnit
589         orgx = orgx - 1;
590         orgy = orgy - 1;
591
592         pcolor = mod(piece, 3) + 1;
593
594         for (y = 0; y < 5; y = y + 1)
595         {
596                 for (x = 0; x < 5; x = x + 1)
597                 {
598                         if (PieceMetric(x, y, rot, piece))
599                         {
600                                 SetSquare(x + orgx, y + orgy, pcolor);
601                         }
602                 }
603         }
604 }
605
606 float LINE_LOW = 349525;
607 float LINE_HIGH = 699050; // above number times 2
608
609 void AddLines(float n)
610 {
611         entity head;
612         if(!self.tet_vs_id)
613                 return;
614         FOR_EACH_REALCLIENT(head) if(head != self) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
615                 head.tet_vs_addlines += n;
616 }
617
618 void CompletedLines()
619 {
620         float y, cleared, ln, added, pos, i;
621
622         cleared = 0;
623         y = TET_LINES;
624         while(y >= 1)
625         {
626                 ln = GetLine(y);
627                 if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)
628                         cleared = cleared + 1;
629                 else
630                         y = y - 1;
631                 ln = GetLine(y - cleared);
632                 SetLine(y, ln);
633         }
634
635         if(cleared >= 4)
636                 AddLines(cleared);
637         else if(cleared >= 1)
638                 AddLines(cleared - 1);
639
640         self.tet_lines = self.tet_lines + cleared;
641         TetAddScore(cleared * cleared * 10);
642
643         added = self.tet_vs_addlines;
644         self.tet_vs_addlines = 0;
645
646         if(added)
647         {
648                 for(y = 1; y <= TET_LINES - added; ++y)
649                 {
650                         SetLine(y, GetLine(y + added));
651                 }
652                 for(y = max(1, TET_LINES - added + 1); y <= TET_LINES; ++y)
653                 {
654                         pos = floor(random() * TET_WIDTH);
655                         ln = 0;
656                         for(i = 1; i <= TET_WIDTH; ++i)
657                                 if(i != pos)
658                                         ln = SetXBlock(i, ln, floor(random() * 3 + 1));
659                         SetLine(y, ln);
660                 }
661         }
662
663         self.tet_highest_line = 0;
664         for(y = 1; y <= TET_LINES; ++y)
665                 if(GetLine(y) != 0)
666                 {
667                         self.tet_highest_line = TET_LINES + 1 - y;
668                         break;
669                 }
670
671         if(added)
672                 tetsnd("tetadd");
673         else if(cleared >= 4)
674                 tetsnd("tetris");
675         else if(cleared)
676                 tetsnd("tetline");
677         else
678                 tetsnd("tetland");
679 };
680
681 void HandleGame(float keyss)
682 {
683
684         // first off, we need to see if we need a new piece
685         vector piece_data;
686         vector check_pos;
687         vector old_pos;
688         float brand_new;
689         float i;
690         brand_new = 0;
691
692
693         if (self.piece_type == 0)
694         {
695                 self.piece_pos = '5 1 0'; // that's about middle top, we count from 1 ARGH
696                 if (self.next_piece)
697                         self.piece_type = self.next_piece;
698                 else
699                         self.piece_type = RandomPiece();
700                 self.next_piece =  RandomPiece();
701                 keyss = 0; // no movement first frame
702                 self.tet_autodown = time + 0.2;
703                 brand_new = 1;
704         }
705         else
706                 ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
707
708         // next we need to check the piece metrics against what's on the level
709         // based on the key order
710
711         old_pos = check_pos = self.piece_pos;
712
713         float nudge;
714         nudge = 0;
715         if (keyss & TETKEY_RIGHT)
716         {
717                 check_pos_x = check_pos_x + 1;
718                 tetsnd("tetmove");
719         }
720         else if (keyss & TETKEY_LEFT)
721         {
722                 check_pos_x = check_pos_x - 1;
723                 tetsnd("tetmove");
724         }
725         else if (keyss & TETKEY_ROTRIGHT)
726         {
727                 check_pos_z = check_pos_z + 1;
728                 piece_data = PieceShape(self.piece_type);
729                 nudge = 1;
730                 tetsnd("tetrot");
731         }
732         else if (keyss & TETKEY_ROTLEFT)
733         {
734                 check_pos_z = check_pos_z - 1;
735                 piece_data = PieceShape(self.piece_type);
736                 nudge = 1;
737                 tetsnd("tetrot");
738         }
739         // bounds check
740         if (check_pos_z > 3)
741                 check_pos_z = 0;
742         else if (check_pos_z < 0)
743                 check_pos_z = 3;
744
745         // reality check
746         if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
747                 self.piece_pos = check_pos;
748         else if (brand_new)
749         {
750                 self.tetris_on = 2;
751                 self.tet_gameovertime = time + 5;
752                 return;
753         }
754         else
755         {
756                 for(i = 1; i <= nudge; ++i)
757                 {
758                         if(CheckMetrics(self.piece_type, check_pos_x + i, check_pos_y, check_pos_z))
759                         {
760                                 self.piece_pos = check_pos + '1 0 0' * i;
761                                 break;
762                         }
763                         else if(CheckMetrics(self.piece_type, check_pos_x - i, check_pos_y, check_pos_z))
764                         {
765                                 self.piece_pos = check_pos - '1 0 0' * i;
766                                 break;
767                         }
768                 }
769         }
770         check_pos = self.piece_pos;
771         if(keyss & TETKEY_DROP)
772         {
773                 // drop to bottom, but do NOT cement it yet
774                 // this allows sliding it
775                 ++check_pos_y;
776                 while(CheckMetrics(self.piece_type, check_pos_x, check_pos_y + 1, check_pos_z))
777                         ++check_pos_y;
778                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
779         }
780         else if (keyss & TETKEY_DOWN)
781         {
782                 check_pos_y = check_pos_y + 1;
783                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
784         }
785         else if (self.tet_autodown < time)
786         {
787                 check_pos_y = check_pos_y + 1;
788                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
789         }
790         if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
791         {
792                 if(old_pos != check_pos)
793                         self.tet_drawtime = 0;
794                 self.piece_pos = check_pos;
795         }
796         else
797         {
798                 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
799                 TetAddScore(1);
800                 CompletedLines();
801                 self.piece_type = 0;
802                 self.tet_drawtime = 0;
803                 return;
804         }
805         CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
806 };
807
808 /*
809 *********************************
810
811 Important Linking Into Quake stuff
812
813 *********************************
814 */
815
816
817 void TetrisImpulse()
818 {
819         if(self.tetris_on == 0 || self.tetris_on == 2) // from "off" or "game over"
820         {
821                 self.tetris_on = 3;
822
823                 if(time < tet_vs_current_timeout)
824                 {
825                         // join VS game
826                         self.tet_vs_id = tet_vs_current_id;
827                 }
828                 else
829                 {
830                         // start new VS game
831                         ++tet_vs_current_id;
832                         tet_vs_current_timeout = time + 15;
833                         self.tet_vs_id = tet_vs_current_id;
834                         bprint("^1TET^7R^1IS: ", self.netname, "^1 started a new game. Do ^7impulse 100^1 to join.\n");
835                 }
836                 self.tet_highest_line = 0;
837                 ResetTetris();
838                 self.tet_org = self.origin;
839                 self.movetype = MOVETYPE_NOCLIP;
840         }
841         else if(self.tetris_on == 1) // from "on"
842         {
843                 Tet_GameExit();
844                 self.impulse = 0;
845         }
846 };
847
848
849 float TetrisPreFrame()
850 {
851         if (!self.tetris_on)
852                 return 0;
853
854         self.tet_org = self.origin;
855         if (self.tet_drawtime > time)
856                 return 1;
857         Draw_Tetris();
858         if(self.tetris_on == 3)
859                 self.tet_drawtime = ceil(time - tet_vs_current_timeout + 0.1) + tet_vs_current_timeout;
860         else
861                 self.tet_drawtime = time + 0.5;
862         return 1;
863 };
864 float frik_anglemoda(float v)
865 {
866         return v - floor(v/360) * 360;
867 };
868 float angcompa(float y1, float y2)
869 {
870         y1 = frik_anglemoda(y1);
871         y2 = frik_anglemoda(y2);
872
873         local float answer;
874         answer = y1 - y2;
875         if (answer > 180)
876                 answer = answer - 360;
877         else if (answer < -180)
878                 answer = answer + 360;
879         return answer;
880 };
881
882 .float tetkey_down, tetkey_rotright, tetkey_left, tetkey_right, tetkey_rotleft, tetkey_drop;
883
884 float TetrisKeyRepeat(.float fld, float f)
885 {
886         if(f)
887         {
888                 if(self.fld == 0) // initial key press
889                 {
890                         self.fld = time + 0.3;
891                         return 1;
892                 }
893                 else if(time > self.fld)
894                 {
895                         self.fld = time + 0.1;
896                         return 1;
897                 }
898                 else
899                 {
900                         // repeating too fast
901                         return 0;
902                 }
903         }
904         else
905         {
906                 self.fld = 0;
907                 return 0;
908         }
909 }
910
911 float TetrisPostFrame()
912 {
913         float keysa;
914
915         keysa = 0;
916
917         if (!self.tetris_on)
918                 return 0;
919
920         if(self.tetris_on == 2 && time > self.tet_gameovertime)
921         {
922                 Tet_GameExit();
923                 return 0;
924         }
925
926         if(self.tetris_on == 3 && time > tet_vs_current_timeout)
927         {
928                 self.tetris_on = 1; // start VS game
929                 self.tet_drawtime = 0;
930         }
931         
932         self.origin = self.tet_org;
933         self.movetype = MOVETYPE_NONE;
934
935         if(self.tetris_on == 1)
936         {
937                 if(TetrisKeyRepeat(tetkey_down, self.movement_x < 0))
938                         keysa |= TETKEY_DOWN;
939
940                 if(TetrisKeyRepeat(tetkey_rotright, self.movement_x > 0))
941                         keysa |= TETKEY_ROTRIGHT;
942
943                 if(TetrisKeyRepeat(tetkey_left, self.movement_y < 0))
944                         keysa |= TETKEY_LEFT;
945
946                 if(TetrisKeyRepeat(tetkey_right, self.movement_y > 0))
947                         keysa |= TETKEY_RIGHT;
948                 
949                 if(TetrisKeyRepeat(tetkey_rotleft, self.BUTTON_CROUCH))
950                         keysa |= TETKEY_ROTLEFT;
951
952                 if(TetrisKeyRepeat(tetkey_drop, self.BUTTON_JUMP))
953                         keysa |= TETKEY_DROP;
954
955                 HandleGame(keysa);
956         }
957
958         return 1;
959 };
960
961 #endif