]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_tetris.qc
bucket-of-seven randomization
[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 '85 0 0'; // I
236         else if (pc == 3)
237                 return '21 1 0'; // L
238         else if (pc == 4)
239                 return '1 21 0'; // J
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.5 1.5 0'; // I
256         else if (pc == 3)
257                 return '2 1 0'; // L
258         else if (pc == 4)
259                 return '2 2 0'; // J
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 += q;
560                         b *= 7;
561                 }
562                 return p + 1;
563         }
564 };
565
566 void TetAddScore(float n)
567 {
568         self.tet_score = self.tet_score + n * Tetris_Level();
569         if (self.tet_score > tet_high_score)
570                 tet_high_score = self.tet_score;
571 };
572 float CheckMetrics(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
573 {
574         // check to see if the piece, if moved to the locations will overlap
575
576         float x, y;
577         // why did I start counting from 1, damnit
578         orgx = orgx - 1;
579         orgy = orgy - 1;
580
581         for (y = 0; y < 5; y = y + 1)
582         {
583                 for (x = 0; x < 5; x = x + 1)
584                 {
585                         if (PieceMetric(x, y, rot, piece))
586                         {
587                                 if (GetSquare(x + orgx, y + orgy))
588                                         return FALSE; // uhoh, gonna hit something.
589                                 if (x+orgx<1 || x+orgx > TET_WIDTH || y+orgy<1 || y+orgy> TET_LINES)
590                                         return FALSE; // ouside the level
591                         }
592                 }
593         }
594         return TRUE;
595 }
596
597 void ClearPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
598 {
599
600         float x, y;
601         // why did I start counting from 1, damnit
602         orgx = orgx - 1;
603         orgy = orgy - 1;
604
605         for (y = 0; y < 5; y = y + 1)
606         {
607                 for (x = 0; x < 5; x = x + 1)
608                 {
609                         if (PieceMetric(x, y, rot, piece))
610                         {
611                                 SetSquare(x + orgx, y + orgy, 0);
612                         }
613                 }
614         }
615 }
616 void CementPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
617 {
618         float pcolor;
619         float x, y;
620         // why did I start counting from 1, damnit
621         orgx = orgx - 1;
622         orgy = orgy - 1;
623
624         pcolor = mod(piece, 3) + 1;
625
626         for (y = 0; y < 5; y = y + 1)
627         {
628                 for (x = 0; x < 5; x = x + 1)
629                 {
630                         if (PieceMetric(x, y, rot, piece))
631                         {
632                                 SetSquare(x + orgx, y + orgy, pcolor);
633                         }
634                 }
635         }
636 }
637
638 float LINE_LOW = 349525;
639 float LINE_HIGH = 699050; // above number times 2
640
641 void AddLines(float n)
642 {
643         entity head;
644         if(!self.tet_vs_id)
645                 return;
646         FOR_EACH_REALCLIENT(head) if(head != self) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
647                 head.tet_vs_addlines += n;
648 }
649
650 void CompletedLines()
651 {
652         float y, cleared, ln, added, pos, i;
653
654         cleared = 0;
655         y = TET_LINES;
656         while(y >= 1)
657         {
658                 ln = GetLine(y);
659                 if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)
660                         cleared = cleared + 1;
661                 else
662                         y = y - 1;
663                 ln = GetLine(y - cleared);
664                 SetLine(y, ln);
665         }
666
667         if(cleared >= 4)
668                 AddLines(cleared);
669         else if(cleared >= 1)
670                 AddLines(cleared - 1);
671
672         self.tet_lines = self.tet_lines + cleared;
673         TetAddScore(cleared * cleared * 10);
674
675         added = self.tet_vs_addlines;
676         self.tet_vs_addlines = 0;
677
678         if(added)
679         {
680                 for(y = 1; y <= TET_LINES - added; ++y)
681                 {
682                         SetLine(y, GetLine(y + added));
683                 }
684                 for(y = max(1, TET_LINES - added + 1); y <= TET_LINES; ++y)
685                 {
686                         pos = floor(random() * TET_WIDTH);
687                         ln = 0;
688                         for(i = 1; i <= TET_WIDTH; ++i)
689                                 if(i != pos)
690                                         ln = SetXBlock(i, ln, floor(random() * 3 + 1));
691                         SetLine(y, ln);
692                 }
693         }
694
695         self.tet_highest_line = 0;
696         for(y = 1; y <= TET_LINES; ++y)
697                 if(GetLine(y) != 0)
698                 {
699                         self.tet_highest_line = TET_LINES + 1 - y;
700                         break;
701                 }
702
703         if(added)
704                 tetsnd("tetadd");
705         else if(cleared >= 4)
706                 tetsnd("tetris");
707         else if(cleared)
708                 tetsnd("tetline");
709         else
710                 tetsnd("tetland");
711 };
712
713 void HandleGame(float keyss)
714 {
715
716         // first off, we need to see if we need a new piece
717         vector piece_data;
718         vector check_pos;
719         vector old_pos;
720         float brand_new;
721         float i;
722         brand_new = 0;
723
724
725         if (self.piece_type == 0)
726         {
727                 self.piece_pos = '5 1 0'; // that's about middle top, we count from 1 ARGH
728                 if (self.next_piece)
729                         self.piece_type = self.next_piece;
730                 else
731                         self.piece_type = RandomPiece();
732                 self.next_piece =  RandomPiece();
733                 keyss = 0; // no movement first frame
734                 self.tet_autodown = time + 0.2;
735                 brand_new = 1;
736         }
737         else
738                 ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
739
740         // next we need to check the piece metrics against what's on the level
741         // based on the key order
742
743         old_pos = check_pos = self.piece_pos;
744
745         float nudge;
746         nudge = 0;
747         if (keyss & TETKEY_RIGHT)
748         {
749                 check_pos_x = check_pos_x + 1;
750                 tetsnd("tetmove");
751         }
752         else if (keyss & TETKEY_LEFT)
753         {
754                 check_pos_x = check_pos_x - 1;
755                 tetsnd("tetmove");
756         }
757         else if (keyss & TETKEY_ROTRIGHT)
758         {
759                 check_pos_z = check_pos_z + 1;
760                 piece_data = PieceShape(self.piece_type);
761                 nudge = 1;
762                 tetsnd("tetrot");
763         }
764         else if (keyss & TETKEY_ROTLEFT)
765         {
766                 check_pos_z = check_pos_z - 1;
767                 piece_data = PieceShape(self.piece_type);
768                 nudge = 1;
769                 tetsnd("tetrot");
770         }
771         // bounds check
772         if (check_pos_z > 3)
773                 check_pos_z = 0;
774         else if (check_pos_z < 0)
775                 check_pos_z = 3;
776
777         // reality check
778         if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
779                 self.piece_pos = check_pos;
780         else if (brand_new)
781         {
782                 self.tetris_on = 2;
783                 self.tet_gameovertime = time + 5;
784                 return;
785         }
786         else
787         {
788                 for(i = 1; i <= nudge; ++i)
789                 {
790                         if(CheckMetrics(self.piece_type, check_pos_x + i, check_pos_y, check_pos_z))
791                         {
792                                 self.piece_pos = check_pos + '1 0 0' * i;
793                                 break;
794                         }
795                         else if(CheckMetrics(self.piece_type, check_pos_x - i, check_pos_y, check_pos_z))
796                         {
797                                 self.piece_pos = check_pos - '1 0 0' * i;
798                                 break;
799                         }
800                 }
801         }
802         check_pos = self.piece_pos;
803         if(keyss & TETKEY_DROP)
804         {
805                 // drop to bottom, but do NOT cement it yet
806                 // this allows sliding it
807                 ++check_pos_y;
808                 while(CheckMetrics(self.piece_type, check_pos_x, check_pos_y + 1, check_pos_z))
809                         ++check_pos_y;
810                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
811         }
812         else if (keyss & TETKEY_DOWN)
813         {
814                 check_pos_y = check_pos_y + 1;
815                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
816         }
817         else if (self.tet_autodown < time)
818         {
819                 check_pos_y = check_pos_y + 1;
820                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
821         }
822         if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
823         {
824                 if(old_pos != check_pos)
825                         self.tet_drawtime = 0;
826                 self.piece_pos = check_pos;
827         }
828         else
829         {
830                 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
831                 TetAddScore(1);
832                 CompletedLines();
833                 self.piece_type = 0;
834                 self.tet_drawtime = 0;
835                 return;
836         }
837         CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
838 };
839
840 /*
841 *********************************
842
843 Important Linking Into Quake stuff
844
845 *********************************
846 */
847
848
849 void TetrisImpulse()
850 {
851         if(self.tetris_on == 0 || self.tetris_on == 2) // from "off" or "game over"
852         {
853                 self.tetris_on = 3;
854
855                 if(time < tet_vs_current_timeout)
856                 {
857                         // join VS game
858                         self.tet_vs_id = tet_vs_current_id;
859                 }
860                 else
861                 {
862                         // start new VS game
863                         ++tet_vs_current_id;
864                         tet_vs_current_timeout = time + 15;
865                         self.tet_vs_id = tet_vs_current_id;
866                         bprint("^1TET^7R^1IS: ", self.netname, "^1 started a new game. Do ^7impulse 100^1 to join.\n");
867                 }
868                 self.tet_highest_line = 0;
869                 ResetTetris();
870                 self.tet_org = self.origin;
871                 self.movetype = MOVETYPE_NOCLIP;
872         }
873         else if(self.tetris_on == 1) // from "on"
874         {
875                 Tet_GameExit();
876                 self.impulse = 0;
877         }
878 };
879
880
881 float TetrisPreFrame()
882 {
883         if (!self.tetris_on)
884                 return 0;
885
886         self.tet_org = self.origin;
887         if (self.tet_drawtime > time)
888                 return 1;
889         Draw_Tetris();
890         if(self.tetris_on == 3)
891                 self.tet_drawtime = ceil(time - tet_vs_current_timeout + 0.1) + tet_vs_current_timeout;
892         else
893                 self.tet_drawtime = time + 0.5;
894         return 1;
895 };
896 float frik_anglemoda(float v)
897 {
898         return v - floor(v/360) * 360;
899 };
900 float angcompa(float y1, float y2)
901 {
902         y1 = frik_anglemoda(y1);
903         y2 = frik_anglemoda(y2);
904
905         local float answer;
906         answer = y1 - y2;
907         if (answer > 180)
908                 answer = answer - 360;
909         else if (answer < -180)
910                 answer = answer + 360;
911         return answer;
912 };
913
914 .float tetkey_down, tetkey_rotright, tetkey_left, tetkey_right, tetkey_rotleft, tetkey_drop;
915
916 float TetrisKeyRepeat(.float fld, float f)
917 {
918         if(f)
919         {
920                 if(self.fld == 0) // initial key press
921                 {
922                         self.fld = time + 0.3;
923                         return 1;
924                 }
925                 else if(time > self.fld)
926                 {
927                         self.fld = time + 0.1;
928                         return 1;
929                 }
930                 else
931                 {
932                         // repeating too fast
933                         return 0;
934                 }
935         }
936         else
937         {
938                 self.fld = 0;
939                 return 0;
940         }
941 }
942
943 float TetrisPostFrame()
944 {
945         float keysa;
946
947         keysa = 0;
948
949         if (!self.tetris_on)
950                 return 0;
951
952         if(self.tetris_on == 2 && time > self.tet_gameovertime)
953         {
954                 Tet_GameExit();
955                 return 0;
956         }
957
958         if(self.tetris_on == 3 && time > tet_vs_current_timeout)
959         {
960                 self.tetris_on = 1; // start VS game
961                 self.tet_drawtime = 0;
962         }
963         
964         self.origin = self.tet_org;
965         self.movetype = MOVETYPE_NONE;
966
967         if(self.tetris_on == 1)
968         {
969                 if(TetrisKeyRepeat(tetkey_down, self.movement_x < 0))
970                         keysa |= TETKEY_DOWN;
971
972                 if(TetrisKeyRepeat(tetkey_rotright, self.movement_x > 0))
973                         keysa |= TETKEY_ROTRIGHT;
974
975                 if(TetrisKeyRepeat(tetkey_left, self.movement_y < 0))
976                         keysa |= TETKEY_LEFT;
977
978                 if(TetrisKeyRepeat(tetkey_right, self.movement_y > 0))
979                         keysa |= TETKEY_RIGHT;
980                 
981                 if(TetrisKeyRepeat(tetkey_rotleft, self.BUTTON_CROUCH))
982                         keysa |= TETKEY_ROTLEFT;
983
984                 if(TetrisKeyRepeat(tetkey_drop, self.BUTTON_JUMP))
985                         keysa |= TETKEY_DROP;
986
987                 HandleGame(keysa);
988         }
989
990         return 1;
991 };
992
993 #endif