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