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