change all function declarations (except builtins and data types) to ANSI C-like...
[divverent/nexuiz.git] / data / qcsrc / server / g_tetris.qc
1 /*
2
3 Installation:
4
5 in cl_impulse.qc add TetrisImpulses(); to ImpulseCommands
6
7 in progs.src add g_tetris.qc after g_subs.qc
8 in cl_client.qc add if (TetrisPreFrame()) return; to  PlayerPreThink
9 in cl_client.qc add if (TetrisPostFrame()) return; to PlayerPostThink
10
11 */
12
13 .vector tet_org;
14
15 .float tetris_on, tet_time, tet_autodown;
16 .vector piece_pos;
17 .float piece_type, next_piece, tet_score, tet_lines;
18
19 var float tet_high_score = 0;
20
21 float TET_LINES = 20;
22 float TET_WIDTH = 10;
23 //character values
24 float TET_BORDER = 132;
25 float TET_BLOCKS = 132; // +1 = first color, +2, +3;
26 float TET_SPACE = 32; // blankness
27
28
29
30 float TETKEY_UP = 1;
31 float TETKEY_DOWN = 2;
32 float TETKEY_LEFT = 4;
33 float TETKEY_RIGHT = 8;
34 float TETKEY_ROTLEFT = 16;
35 float TETKEY_ROTRIGHT = 32;
36
37 float PIECES = 7;
38
39 .float line1, line2, line3, line4, line5, line6, line7,
40 line8, line9, line10, line11, line12, line13, line14, line15,
41 line16, line17, line18, line19, line20;
42
43
44 float   SVC_CENTERPRINTa                = 26;
45
46
47 /*
48 *********************************
49
50 Library Functions
51
52 *********************************
53 */
54 void SetLine(float ln, float vl)
55 {
56         if (ln == 1)
57                 self.line1 = vl;
58         else if (ln == 2)
59                 self.line2 = vl;
60         else if (ln == 3)
61                 self.line3 = vl;
62         else if (ln == 4)
63                 self.line4 = vl;
64         else if (ln == 5)
65                 self.line5 = vl;
66         else if (ln == 6)
67                 self.line6 = vl;
68         else if (ln == 7)
69                 self.line7 = vl;
70         else if (ln == 8)
71                 self.line8 = vl;
72         else if (ln == 9)
73                 self.line9 = vl;
74         else if (ln == 10)
75                 self.line10 = vl;
76         else if (ln == 11)
77                 self.line11 = vl;
78         else if (ln == 12)
79                 self.line12 = vl;
80         else if (ln == 13)
81                 self.line13 = vl;
82         else if (ln == 14)
83                 self.line14 = vl;
84         else if (ln == 15)
85                 self.line15 = vl;
86         else if (ln == 16)
87                 self.line16 = vl;
88         else if (ln == 17)
89                 self.line17 = vl;
90         else if (ln == 18)
91                 self.line18 = vl;
92         else if (ln == 19)
93                 self.line19 = vl;
94         else if (ln == 20)
95                 self.line20 = vl;
96 };
97
98 float GetLine(float ln)
99 {
100         if (ln == 1)
101                 return self.line1;
102         else if (ln == 2)
103                 return self.line2;
104         else if (ln == 3)
105                 return self.line3;
106         else if (ln == 4)
107                 return self.line4;
108         else if (ln == 5)
109                 return self.line5;
110         else if (ln == 6)
111                 return self.line6;
112         else if (ln == 7)
113                 return self.line7;
114         else if (ln == 8)
115                 return self.line8;
116         else if (ln == 9)
117                 return self.line9;
118         else if (ln == 10)
119                 return self.line10;
120         else if (ln == 11)
121                 return self.line11;
122         else if (ln == 12)
123                 return self.line12;
124         else if (ln == 13)
125                 return self.line13;
126         else if (ln == 14)
127                 return self.line14;
128         else if (ln == 15)
129                 return self.line15;
130         else if (ln == 16)
131                 return self.line16;
132         else if (ln == 17)
133                 return self.line17;
134         else if (ln == 18)
135                 return self.line18;
136         else if (ln == 19)
137                 return self.line19;
138         else if (ln == 20)
139                 return self.line20;
140         else
141                 return 0;
142 };
143
144 float GetXBlock(float x, float dat)
145 {
146         if (x == 1)
147                 return dat & 3;
148         else if (x == 2)
149                 return (dat & 12) / 4;
150         else if (x == 3)
151                 return (dat & 48) / 16;
152         else if (x == 4)
153                 return (dat & 192) / 64;
154         else if (x == 5)
155                 return (dat & 768) / 256;
156         else if (x == 6)
157                 return (dat & 3072) / 1024;
158         else if (x == 7)
159                 return (dat & 12288) / 4096;
160         else if (x == 8)
161                 return (dat & 49152) / 16384;
162         else if (x == 9)
163                 return (dat & 196608) / 65536;
164         else if (x == 10)
165                 return (dat & 786432) / 262144;
166         else
167                 return 0;
168 };
169
170 float SetXBlock(float x, float dat, float new)
171 {
172         if (x == 1)
173                 return (dat - (dat & 3)) | new;
174         else if (x == 2)
175                 return (dat - (dat & 12)) | (new*4);
176         else if (x == 3)
177                 return (dat - (dat & 48)) | (new*16);
178         else if (x == 4)
179                 return (dat - (dat & 192)) | (new*64);
180         else if (x == 5)
181                 return (dat - (dat & 768)) | (new*256);
182         else if (x == 6)
183                 return (dat - (dat & 3072)) | (new*1024);
184         else if (x == 7)
185                 return (dat - (dat & 12288)) | (new*4096);
186         else if (x == 8)
187                 return (dat - (dat & 49152)) | (new*16384);
188         else if (x == 9)
189                 return (dat - (dat & 196608)) | (new*65536);
190         else if (x == 10)
191                 return (dat - (dat & 786432)) | (new*262144);
192         else
193                 return dat;
194 };
195
196
197 float GetSquare(float x, float y)
198 {
199         return GetXBlock(x,  GetLine(y));
200 };
201
202 void SetSquare(float x, float y, float val)
203 {
204         float dat;
205
206         dat = GetLine(y);
207         dat  = SetXBlock(x, dat, val & 3);
208         SetLine(y, dat);
209 };
210
211
212
213 vector PieceShape(float pc)
214 {
215
216 /*
217 1 =
218  ##
219  ##
220 */
221         if (pc == 1)
222                 return '20 20 0'; // 1 * 4 + 1 * 16
223 /*
224 2 =
225
226 ####
227 */
228         else if (pc == 2)
229                 return '85 0 0';
230
231 /*
232 3 =
233
234 ###
235 #
236 */
237         else if (pc == 3)
238                 return '21 1 0';
239 /*
240 4 =
241
242 #
243 ###
244 */
245         else if (pc == 4)
246                 return '1 21 0';
247 /*
248 5 =
249 ##
250  ##
251 */
252         else if (pc == 5)
253                 return '5 20 0';
254
255 /*
256 6 =
257  ##
258 ##
259 */
260         else if (pc == 6)
261                 return '20 5 0';
262
263 /*
264 7 =
265  #
266 ###
267 */
268         else if (pc == 7)
269                 return '4 21 0';
270
271
272         else
273                 return '0 0 0';
274
275 }
276
277 // do x 1..4 and y 1..4 in case of rotation
278 float PieceMetric(float x, float y, float rot, float pc)
279 {
280         float t;
281         vector piece_dat;
282
283         // return bits of a piece
284         if (rot == 1) // 90 degrees
285         {
286                 t = y;
287                 y = x;
288                 x = 5 - t;
289         }
290         else if (rot == 2)//180
291         {
292                 x = 5 - x;
293                 y = 3 - y;
294         }
295         else if (rot == 3) // 270
296         {
297                 t = y;
298                 y = 5 - x;
299                 x = t;
300         }
301         if (x < 1 || y < 1 || x > 4 || y > 2)
302                 return 0;
303         piece_dat = PieceShape(pc);
304         if (y == 1)
305                 return GetXBlock(x, piece_dat_x); // first row
306         else if (y == 2)
307                 return GetXBlock(x, piece_dat_y); // second row
308         else if (y == 3)
309                 return GetXBlock(x, piece_dat_z); // third row (doesn't exist)
310         else
311                 return 0; // illegal parms
312 };
313 /*
314 *********************************
315
316 Draw
317
318 *********************************
319 */
320
321
322 /* some prydon gate functions to make life easier....
323
324 somewhat modified because we don't need all the fanciness Prydon Gate is capable of
325
326 */
327
328 // remove warnings by adding underscores, i hate warnings, warnings suck
329 // --blub
330 void p6(float _c1, float _c2, float _c3, float _c4, float c5, float c6)
331 {
332         WriteChar(MSG_ONE, _c1);
333         WriteChar(MSG_ONE, _c2);
334         WriteChar(MSG_ONE, _c3);
335         WriteChar(MSG_ONE, _c4);
336         WriteChar(MSG_ONE, c5);
337         WriteChar(MSG_ONE, c6);
338 };
339
340 float pnum(float num, float dig)
341 {
342         local float f, i;
343         if (num < 0)
344         {
345                 WriteChar(MSG_ONE, 45);
346                 num = 0 - num;
347         }
348         f = floor(num / 10);
349         num = num - (f * 10);
350         if (f)
351                 dig = pnum(f, dig+1);
352         else
353         {
354                 // pad to 6
355                 for (i = 0; i < (5 - dig); i = i + 1)
356                         WriteChar(MSG_ONE, TET_SPACE);
357         }
358         WriteChar(MSG_ONE, 48 + num);
359         return dig;
360 };
361
362 void DrawLine(float ln)
363 {
364         float x, d;
365         WriteChar(MSG_ONE, TET_BORDER);
366
367         for (x = 1; x <= TET_WIDTH; x = x + 1)
368         {
369                 d = GetSquare(x, ln);
370                 if (d)
371                         WriteChar(MSG_ONE, TET_BLOCKS + d);
372                 else
373                         WriteChar(MSG_ONE, TET_SPACE);
374         }
375         WriteChar(MSG_ONE, TET_BORDER);
376 }
377
378 void DrawPiece(float pc, float ln)
379 {
380         float x, d, piece_ln, pcolor;
381         vector piece_dat;
382         pcolor = pc & 3;
383         if (pcolor == 0) // 4
384                 pcolor = 1;
385         WriteChar(MSG_ONE, TET_SPACE); // pad to 6
386
387         piece_dat = PieceShape(pc);
388         if (ln == 1)
389                 piece_ln = piece_dat_x;
390         else
391                 piece_ln = piece_dat_y;
392         for (x = 1; x <= 4; x = x + 1)
393         {
394                 d = GetXBlock(x, piece_ln) * pcolor;
395                 if (d)
396                         WriteChar(MSG_ONE, TET_BLOCKS + d);
397                 else
398                         WriteChar(MSG_ONE, TET_SPACE);
399         }
400         WriteChar(MSG_ONE, TET_SPACE);  // pad to 6
401 }
402 void Draw_Tetris()
403 {
404         float i;
405         msg_entity = self;
406         WriteChar(MSG_ONE, SVC_CENTERPRINTa);
407         // decoration
408         for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
409                 WriteChar(MSG_ONE, TET_BORDER);
410         p6(' ', ' ', ' ', ' ', ' ', ' ');
411         WriteChar(MSG_ONE, 10);
412         for (i = 1; i <= TET_LINES; i = i + 1)
413         {
414                 DrawLine(i);
415                 if (i == 1)
416                         p6(' ', 'N', 'E', 'X', 'T', ' ');
417                 else if (i == 3)
418                         DrawPiece(self.next_piece, 1);
419                 else if (i == 4)
420                         DrawPiece(self.next_piece, 2);
421                 else if (i == 6)
422                         p6(' ', 'L', 'I', 'N', 'E', 'S');
423                 else if (i == 7)
424                         pnum(self.tet_lines, 0);
425                 else if (i == 9)
426                         p6(' ', 'S', 'C', 'O', 'R', 'E');
427                 else if (i == 10)
428                         pnum(self.tet_score, 0);
429                 else if (i == 12)
430                         p6(' ', 'H', 'I', 'G', 'H', ' ');
431                 else if (i == 13)
432                         p6(' ', 'S', 'C', 'O', 'R', 'E');
433                 else if (i == 14)
434                         pnum(tet_high_score, 0);
435                 else if (i == 16)
436                         p6(' ', 'L', 'E', 'V', 'E', 'L');
437                 else if (i == 17)
438                         pnum(floor(self.tet_lines / 20)+ 1, 0);
439                 else
440                         p6(' ', ' ', ' ', ' ', ' ', ' ');
441                 WriteChar(MSG_ONE, 10);
442         }
443         // decoration
444
445         for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
446                 WriteChar(MSG_ONE, TET_BORDER);
447         p6(' ', ' ', ' ', ' ', ' ', ' ');
448         WriteChar(MSG_ONE, 10);
449         WriteChar(MSG_ONE, 0);
450 }
451 /*
452 *********************************
453
454 Game Functions
455
456 *********************************
457 */
458
459 // reset the game
460 void ResetTetris()
461 {
462         float i;
463
464         for (i=1; i<=TET_LINES; i = i + 1)
465                 SetLine(i, 0);
466         self.piece_pos = '0 0 0';
467         self.piece_type = 0;
468         self.next_piece = self.tet_lines = self.tet_score = 0;
469
470 };
471
472 void Tet_GameOver()
473 {
474         centerprint(self, "Game Over");
475         self.tetris_on = 0;
476         ResetTetris();
477         self.movetype = MOVETYPE_WALK;
478 };
479
480
481
482 /*
483 *********************************
484
485 Game Mechanics
486
487 *********************************
488 */
489 float RandomPiece()
490 {
491         return floor(random() * PIECES) + 1;
492 };
493
494 void TetAddScore(float n)
495 {
496         self.tet_score = self.tet_score + n;
497         if (self.tet_score > tet_high_score)
498                 tet_high_score = self.tet_score;
499 };
500 float CheckMetrics(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
501 {
502         // check to see if the piece, if moved to the locations will overlap
503
504         float x, y;
505         // why did I start counting from 1, damnit
506         orgx = orgx - 1;
507         orgy = orgy - 1;
508
509         for (y = 1; y < 5; y = y + 1)
510         {
511                 for (x = 1; x < 5; x = x + 1)
512                 {
513                         if (PieceMetric(x, y, rot, piece))
514                         {
515                                 if (GetSquare(x + orgx, y + orgy))
516                                         return FALSE; // uhoh, gonna hit something.
517                                 if (x+orgx<1 || x+orgx > TET_WIDTH || y+orgy<1 || y+orgy> TET_LINES)
518                                         return FALSE; // ouside the level
519                         }
520                 }
521         }
522         return TRUE;
523 }
524
525 void ClearPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
526 {
527
528         float x, y;
529         // why did I start counting from 1, damnit
530         orgx = orgx - 1;
531         orgy = orgy - 1;
532
533         for (y = 1; y < 5; y = y + 1)
534         {
535                 for (x = 1; x < 5; x = x + 1)
536                 {
537                         if (PieceMetric(x, y, rot, piece))
538                         {
539                                 SetSquare(x + orgx, y + orgy, 0);
540                         }
541                 }
542         }
543 }
544 void CementPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
545 {
546         float pcolor;
547         float x, y;
548         // why did I start counting from 1, damnit
549         orgx = orgx - 1;
550         orgy = orgy - 1;
551
552         pcolor = piece & 3;
553         if (pcolor == 0) // 4
554                 pcolor = 1;
555
556         for (y = 1; y < 5; y = y + 1)
557         {
558                 for (x = 1; x < 5; x = x + 1)
559                 {
560                         if (PieceMetric(x, y, rot, piece))
561                         {
562                                 SetSquare(x + orgx, y + orgy, pcolor);
563                         }
564                 }
565         }
566 }
567
568 float LINE_LOW = 349525;
569 float LINE_HIGH = 699050; // above number times 2
570
571 void CompletedLines()
572 {
573         float y, cleared, ln;
574
575         cleared = 0;
576         y = TET_LINES;
577         while(y >= 1)
578         {
579                 ln = GetLine(y);
580                 if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)
581                         cleared = cleared + 1;
582                 else
583                         y = y - 1;
584                 ln = GetLine(y - cleared);
585                 SetLine(y, ln);
586         }
587         self.tet_lines = self.tet_lines + cleared;
588         TetAddScore(cleared * cleared * 10);
589 };
590
591 void HandleGame(float keyss)
592 {
593
594         // first off, we need to see if we need a new piece
595         vector check_pos;
596         float brand_new;
597         brand_new = 0;
598         if (self.tet_time > time)
599                 return;
600         self.tet_time = time + 0.1;
601
602
603         if (self.piece_type == 0)
604         {
605                 self.piece_pos = '5 1 0'; // that's about middle top, we count from 1 ARGH
606                 if (self.next_piece)
607                         self.piece_type = self.next_piece;
608                 else
609                         self.piece_type = RandomPiece();
610                 self.next_piece =  RandomPiece();
611                 keyss = 0; // no movement first frame
612                 self.tet_autodown = time + 0.2;
613                 brand_new = 1;
614         }
615         else
616                 ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
617
618         // next we need to check the piece metrics against what's on the level
619         // based on the key order
620
621         check_pos = self.piece_pos;
622
623         if (keyss & TETKEY_RIGHT)
624                 check_pos_x = check_pos_x + 1;
625         else if (keyss & TETKEY_LEFT)
626                 check_pos_x = check_pos_x - 1;
627         else if (keyss & TETKEY_ROTRIGHT)
628                 check_pos_z = check_pos_z + 1;
629         else if (keyss & TETKEY_ROTLEFT)
630                 check_pos_z = check_pos_z - 1;
631         // bounds check
632         if (check_pos_z > 3)
633                 check_pos_z = 0;
634         else if (check_pos_z < 0)
635                 check_pos_z = 3;
636
637         // reality check
638         if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
639                 self.piece_pos = check_pos;
640         else if (brand_new)
641                 Tet_GameOver();
642         check_pos = self.piece_pos;
643         if (keyss & TETKEY_DOWN)
644                 check_pos_y = check_pos_y + 1;
645         else if (self.tet_autodown < time)
646         {
647                 check_pos_y = check_pos_y + 1;
648                 self.tet_autodown = time + 1 / (floor(self.tet_lines / 20) + 1);
649         }
650         if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
651                 self.piece_pos = check_pos;
652         else
653         {
654                 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
655                 TetAddScore(1);
656                 CompletedLines();
657                 self.piece_type = 0;
658                 return;
659         }
660         CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
661 };
662
663 /*
664 *********************************
665
666 Important Linking Into Quake stuff
667
668 *********************************
669 */
670
671
672 void TetrisImpulses(float imp)
673 {
674         if (imp == 200)
675         {
676                 self.tetris_on = 1;
677                 ResetTetris();
678                 self.tet_org = self.origin;
679                 self.movetype = MOVETYPE_NOCLIP;
680                 stuffcmd(self, "cl_bob 0\ncl_rollangle 0\n");
681         }
682 };
683
684
685 float TetrisPreFrame()
686 {
687         if (!self.tetris_on)
688                 return 0;
689
690         self.tet_org = self.origin;
691         if (self.tet_time > time)
692                 return 1;
693         Draw_Tetris();
694         return 1;
695
696 };
697 float frik_anglemoda(float v)
698 {
699         return v - floor(v/360) * 360;
700 };
701 float angcompa(float y1, float y2)
702 {
703         y1 = frik_anglemoda(y1);
704         y2 = frik_anglemoda(y2);
705
706         local float answer;
707         answer = y1 - y2;
708         if (answer > 180)
709                 answer = answer - 360;
710         else if (answer < -180)
711                 answer = answer + 360;
712         return answer;
713 };
714
715
716 float TetrisPostFrame()
717 {
718         vector mov;
719         float keysa, norm;
720
721         keysa = 0;
722
723         if (!self.tetris_on)
724                 return 0;
725         
726         if (self.origin != self.tet_org)
727         {
728                 self.origin = self.tet_org;
729                 if(self.movement_x < 0)
730                         keysa |= TETKEY_DOWN;
731                 else if(self.movement_x > 0)
732                         keysa |= TETKEY_UP;
733                 if(self.movement_y < 0)
734                         keysa |= TETKEY_LEFT;
735                 else if(self.movement_y > 0)
736                         keysa |= TETKEY_RIGHT;
737         }
738         if (self.BUTTON_ATCK)
739                 keysa = keysa | TETKEY_ROTRIGHT;
740         if (self.BUTTON_JUMP)
741                 keysa = keysa | TETKEY_ROTLEFT;
742         HandleGame(keysa);
743         return 1;
744 };
745