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