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