]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/g_tetris.qc
Merge commit 'origin/parasti/uncrlf' into divVerent/bastet
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / g_tetris.qc
1 /*
2
3 Installation:
4
5 compile with -DTETRIS
6
7 */
8
9 #ifdef TETRIS
10
11 float autocvar_g_bastet;
12 .vector tet_org;
13
14 float tet_vs_current_id;
15 float tet_vs_current_timeout;
16 .float tet_vs_id, tet_vs_addlines;
17 .float tet_highest_line;
18 .float tetris_on, tet_gameovertime, tet_drawtime, tet_autodown;
19 .vector piece_pos;
20 .float piece_type, next_piece, tet_score, tet_lines;
21 .float tet_piece_bucket;
22
23 // tetris_on states:
24 //   1 = running
25 //   2 = game over
26 //   3 = waiting for VS players
27
28 var float tet_high_score = 0;
29
30 vector TET_START_PIECE_POS = '5 1 0';
31 float TET_LINES = 20;
32 float TET_WIDTH = 10;
33 //character values
34 float TET_BORDER = 139;
35 float TET_BLOCK = 133;
36 float TET_SPACE = 160; // blankness
37
38
39
40 float TETKEY_UP = 1;
41 float TETKEY_DOWN = 2;
42 float TETKEY_LEFT = 4;
43 float TETKEY_RIGHT = 8;
44 float TETKEY_ROTLEFT = 16;
45 float TETKEY_ROTRIGHT = 32;
46 float TETKEY_DROP = 64;
47 string TET_PADDING_RIGHT = "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"; // get away from crosshair
48
49 float PIECES = 7;
50
51 .float line1, line2, line3, line4, line5, line6, line7,
52 line8, line9, line10, line11, line12, line13, line14, line15,
53 line16, line17, line18, line19, line20;
54
55
56 float   SVC_CENTERPRINTa                = 26;
57
58 float Tetris_Level()
59 {
60         return ((floor((self.tet_lines / 10)) + 1));
61 };
62
63 void tetsnd(string snd)
64 {
65         play2(self, strcat("sounds/tetris/", snd));
66 }
67
68 /*
69 *********************************
70
71 Library Functions
72
73 *********************************
74 */
75 void SetLine(float ln, float vl)
76 {
77         if (ln == 1)
78                 self.line1 = vl;
79         else if (ln == 2)
80                 self.line2 = vl;
81         else if (ln == 3)
82                 self.line3 = vl;
83         else if (ln == 4)
84                 self.line4 = vl;
85         else if (ln == 5)
86                 self.line5 = vl;
87         else if (ln == 6)
88                 self.line6 = vl;
89         else if (ln == 7)
90                 self.line7 = vl;
91         else if (ln == 8)
92                 self.line8 = vl;
93         else if (ln == 9)
94                 self.line9 = vl;
95         else if (ln == 10)
96                 self.line10 = vl;
97         else if (ln == 11)
98                 self.line11 = vl;
99         else if (ln == 12)
100                 self.line12 = vl;
101         else if (ln == 13)
102                 self.line13 = vl;
103         else if (ln == 14)
104                 self.line14 = vl;
105         else if (ln == 15)
106                 self.line15 = vl;
107         else if (ln == 16)
108                 self.line16 = vl;
109         else if (ln == 17)
110                 self.line17 = vl;
111         else if (ln == 18)
112                 self.line18 = vl;
113         else if (ln == 19)
114                 self.line19 = vl;
115         else if (ln == 20)
116                 self.line20 = vl;
117 };
118
119 float GetLine(float ln)
120 {
121         if (ln == 1)
122                 return self.line1;
123         else if (ln == 2)
124                 return self.line2;
125         else if (ln == 3)
126                 return self.line3;
127         else if (ln == 4)
128                 return self.line4;
129         else if (ln == 5)
130                 return self.line5;
131         else if (ln == 6)
132                 return self.line6;
133         else if (ln == 7)
134                 return self.line7;
135         else if (ln == 8)
136                 return self.line8;
137         else if (ln == 9)
138                 return self.line9;
139         else if (ln == 10)
140                 return self.line10;
141         else if (ln == 11)
142                 return self.line11;
143         else if (ln == 12)
144                 return self.line12;
145         else if (ln == 13)
146                 return self.line13;
147         else if (ln == 14)
148                 return self.line14;
149         else if (ln == 15)
150                 return self.line15;
151         else if (ln == 16)
152                 return self.line16;
153         else if (ln == 17)
154                 return self.line17;
155         else if (ln == 18)
156                 return self.line18;
157         else if (ln == 19)
158                 return self.line19;
159         else if (ln == 20)
160                 return self.line20;
161         else
162                 return 0;
163 };
164
165 float GetXBlock(float x, float dat)
166 {
167         if (x == 1)
168                 return dat & 3;
169         else if (x == 2)
170                 return (dat & 12) / 4;
171         else if (x == 3)
172                 return (dat & 48) / 16;
173         else if (x == 4)
174                 return (dat & 192) / 64;
175         else if (x == 5)
176                 return (dat & 768) / 256;
177         else if (x == 6)
178                 return (dat & 3072) / 1024;
179         else if (x == 7)
180                 return (dat & 12288) / 4096;
181         else if (x == 8)
182                 return (dat & 49152) / 16384;
183         else if (x == 9)
184                 return (dat & 196608) / 65536;
185         else if (x == 10)
186                 return (dat & 786432) / 262144;
187         else
188                 return 0;
189 };
190
191 float SetXBlock(float x, float dat, float new)
192 {
193         if (x == 1)
194                 return (dat - (dat & 3)) | new;
195         else if (x == 2)
196                 return (dat - (dat & 12)) | (new*4);
197         else if (x == 3)
198                 return (dat - (dat & 48)) | (new*16);
199         else if (x == 4)
200                 return (dat - (dat & 192)) | (new*64);
201         else if (x == 5)
202                 return (dat - (dat & 768)) | (new*256);
203         else if (x == 6)
204                 return (dat - (dat & 3072)) | (new*1024);
205         else if (x == 7)
206                 return (dat - (dat & 12288)) | (new*4096);
207         else if (x == 8)
208                 return (dat - (dat & 49152)) | (new*16384);
209         else if (x == 9)
210                 return (dat - (dat & 196608)) | (new*65536);
211         else if (x == 10)
212                 return (dat - (dat & 786432)) | (new*262144);
213         else
214                 return dat;
215 };
216
217
218 float GetSquare(float x, float y)
219 {
220         return GetXBlock(x,  GetLine(y));
221 };
222
223 void SetSquare(float x, float y, float val)
224 {
225         float dat;
226
227         dat = GetLine(y);
228         dat  = SetXBlock(x, dat, val & 3);
229         SetLine(y, dat);
230 };
231
232 vector PieceShape(float pc)
233 {
234         if (pc == 1)
235                 return '5 5 0'; // O
236         else if (pc == 2)
237                 return '1 21 0'; // J
238         else if (pc == 3)
239                 return '21 1 0'; // L
240         else if (pc == 4)
241                 return '85 0 0'; // I
242         else if (pc == 5)
243                 return '5 20 0'; // Z
244         else if (pc == 6)
245                 return '20 5 0'; // S
246         else if (pc == 7)
247                 return '4 21 0'; // T
248         else
249                 return '0 0 0';
250 }
251
252 vector PieceCenter(float pc)
253 {
254         if(pc == 1)
255                 return '1.5 1.5 0'; // O
256         else if (pc == 2)
257                 return '2 2 0'; // J
258         else if (pc == 3)
259                 return '2 1 0'; // L
260         else if (pc == 4)
261                 return '2.5 1.5 0'; // I
262         else if (pc == 5)
263                 return '2 2 0'; // Z
264         else if (pc == 6)
265                 return '2 2 0'; // S
266         else if (pc == 7)
267                 return '2 2 0'; // T
268         else
269                 return '0 0 0';
270 }
271
272 // do x 1..4 and y 1..4 in case of rotation
273 float PieceMetric(float x, float y, float rot, float pc)
274 {
275         float t;
276         vector piece_dat;
277         float wid;
278
279         // return bits of a piece
280         wid = piece_dat_z + 1;
281         piece_dat = PieceCenter(pc);
282         if (rot == 1) // 90 degrees
283         {
284                 // x+cx, y+cy -> -y+cx, x+cy
285                 // X, Y       -> -Y+cy+cx, X-cx+cy
286                 //   x = X-cx
287                 //   y = Y-cy
288                 t = y;
289                 y = x - piece_dat_x + piece_dat_y;
290                 x = -t + piece_dat_x + piece_dat_y;
291         }
292         else if (rot == 2)//180
293         {
294                 x = 2 * piece_dat_x - x;
295                 y = 2 * piece_dat_y - y;
296         }
297         else if (rot == 3) // 270
298         {
299                 // x+cx, y+cy -> y+cx, -x+cy
300                 // X, Y       -> Y-cy+cx, -X+cx+cy
301                 //   x = X-cx
302                 //   y = Y-cy
303                 t = y;
304                 y = -x + piece_dat_y + piece_dat_x;
305                 x =  t - piece_dat_y + piece_dat_x;
306         }
307         if (x < 1 || y < 1 || x > 4 || y > 2)
308                 return 0;
309         piece_dat = PieceShape(pc);
310         if (y == 1)
311                 return GetXBlock(x, piece_dat_x); // first row
312         else if (y == 2)
313                 return GetXBlock(x, piece_dat_y); // second row
314         else
315                 return 0; // illegal parms
316 };
317 /*
318 *********************************
319
320 Draw
321
322 *********************************
323 */
324
325
326 /* some prydon gate functions to make life easier....
327
328 somewhat modified because we don't need all the fanciness Prydon Gate is capable of
329
330 */
331
332 void WriteTetrisString(string s)
333 {
334         WriteUnterminatedString(MSG_ONE, strconv(0, 2, 2, s));
335 }
336
337 float pnum(float num, float dig)
338 {
339         local float f, i;
340         if (num < 0)
341         {
342                 WriteChar(MSG_ONE, 173);
343                 num = 0 - num;
344         }
345         f = floor(num / 10);
346         num = num - (f * 10);
347         if (f)
348                 dig = pnum(f, dig+1);
349         else
350         {
351                 // pad to 6
352                 for (i = 0; i < (5 - dig); i = i + 1)
353                         WriteChar(MSG_ONE, TET_SPACE);
354         }
355         WriteChar(MSG_ONE, 176 + num);
356         return dig;
357 };
358
359 void DrawLine(float ln)
360 {
361         float x, d;
362         WriteChar(MSG_ONE, TET_BORDER);
363
364         for (x = 1; x <= TET_WIDTH; x = x + 1)
365         {
366                 d = GetSquare(x, ln);
367                 if (d)
368                 {
369                         WriteChar(MSG_ONE, '^');
370                         WriteChar(MSG_ONE, d * d - 2 * d + 50); // 1, 2, 5
371                         WriteChar(MSG_ONE, TET_BLOCK);
372                 }
373                 else
374                         WriteChar(MSG_ONE, TET_SPACE);
375         }
376         WriteChar(MSG_ONE, '^');
377         WriteChar(MSG_ONE, '7');
378         WriteChar(MSG_ONE, TET_BORDER);
379 }
380
381 void DrawPiece(float pc, float ln)
382 {
383         float x, d, piece_ln, pcolor;
384         vector piece_dat;
385         pcolor = mod(pc, 3) + 1;
386         WriteChar(MSG_ONE, TET_SPACE); // pad to 6
387
388         piece_dat = PieceShape(pc);
389         if (ln == 1)
390                 piece_ln = piece_dat_x;
391         else
392                 piece_ln = piece_dat_y;
393         for (x = 1; x <= 4; x = x + 1)
394         {
395                 d = GetXBlock(x, piece_ln) * pcolor;
396                 if (d)
397                 {
398                         WriteChar(MSG_ONE, '^');
399                         WriteChar(MSG_ONE, d * d - 2 * d + 50); // 1, 2, 5
400                         WriteChar(MSG_ONE, TET_BLOCK);
401                 }
402                 else
403                         WriteChar(MSG_ONE, TET_SPACE);
404         }
405         WriteChar(MSG_ONE, TET_SPACE);  // pad to 6
406 }
407 void Draw_Tetris()
408 {
409         float i;
410         entity head;
411         msg_entity = self;
412         WriteChar(MSG_ONE, SVC_CENTERPRINTa);
413         if(autocvar_g_bastet)
414         {
415                 WriteTetrisString("NEVER GONNA GIVE YOU");
416                 WriteChar(MSG_ONE, 10);
417         }
418         // decoration
419         for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
420                 WriteChar(MSG_ONE, TET_BORDER);
421         WriteTetrisString("      ");
422         WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
423         WriteChar(MSG_ONE, 10);
424         for (i = 1; i <= TET_LINES; i = i + 1)
425         {
426                 if(self.tetris_on == 2)
427                         WriteTetrisString(" GAME  OVER ");
428                 else if(self.tetris_on == 3)
429                         WriteTetrisString("PLEASE  WAIT");
430                 else
431                         DrawLine(i);
432                 if (i == 1)
433                         WriteTetrisString(autocvar_g_bastet ? " THAT " : " NEXT ");
434                 else if (i == 3)
435                         DrawPiece(self.next_piece, 1);
436                 else if (i == 4)
437                         DrawPiece(self.next_piece, 2);
438                 else if (i == 6)
439                         WriteTetrisString(" LINES");
440                 else if (i == 7)
441                         pnum(self.tet_lines, 0);
442                 else if (i == 9)
443                         WriteTetrisString(" SCORE");
444                 else if (i == 10)
445                         pnum(self.tet_score, 0);
446                 else if (i == 12)
447                         WriteTetrisString(" HIGH ");
448                 else if (i == 13)
449                         WriteTetrisString(" SCORE");
450                 else if (i == 14)
451                         pnum(tet_high_score, 0);
452                 else if (i == 16)
453                         WriteTetrisString(" LEVEL");
454                 else if (i == 17)
455                         pnum(Tetris_Level(), 0);
456                 else
457                         WriteTetrisString("      ");
458                 WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
459                 WriteChar(MSG_ONE, 10);
460         }
461         // decoration
462
463         for (i = 1; i <= (TET_WIDTH + 2); i = i + 1)
464                 WriteChar(MSG_ONE, TET_BORDER);
465         WriteTetrisString("      ");
466         WriteUnterminatedString(MSG_ONE, TET_PADDING_RIGHT);
467         WriteChar(MSG_ONE, 10);
468
469         // VS game status
470         if(self.tet_vs_id)
471         {
472                 WriteChar(MSG_ONE, 10);
473                 WriteChar(MSG_ONE, 10);
474                 if(self.tetris_on == 3)
475                 {
476                         WriteUnterminatedString(MSG_ONE, strcat("WAITING FOR OTHERS (", ftos(ceil(tet_vs_current_timeout - time)), " SEC)\n"));
477                 }
478
479                 WriteChar(MSG_ONE, 10);
480                 FOR_EACH_REALCLIENT(head) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
481                 {
482                         if(head == self)
483                                 WriteUnterminatedString(MSG_ONE, ">");
484                         else
485                                 WriteUnterminatedString(MSG_ONE, " ");
486                         if(head.tetris_on == 2)
487                                 WriteUnterminatedString(MSG_ONE, "   X_X");
488                         else
489                                 pnum(head.tet_highest_line, 0);
490                         WriteUnterminatedString(MSG_ONE, " ");
491                         WriteUnterminatedString(MSG_ONE, head.netname);
492                         WriteChar(MSG_ONE, 10);
493                 }
494         }
495
496         WriteChar(MSG_ONE, 0);
497 }
498 /*
499 *********************************
500
501 Game Functions
502
503 *********************************
504 */
505
506 // reset the game
507 void ResetTetris()
508 {
509         float i;
510
511         for (i=1; i<=TET_LINES; i = i + 1)
512                 SetLine(i, 0);
513         self.piece_pos = '0 0 0';
514         self.piece_type = 0;
515         self.next_piece = self.tet_lines = self.tet_score = 0;
516         self.tet_piece_bucket = 0;
517 };
518
519 void Tet_GameExit()
520 {
521         centerprint(self, " ");
522         self.tetris_on = 0;
523         self.tet_vs_id = 0;
524         ResetTetris();
525         self.movetype = MOVETYPE_WALK;
526 };
527
528 void PrintField()
529 {
530         float l;
531         float r, c;
532         for(r = 1; r <= TET_LINES; ++r)
533         {
534                 l = GetLine(r);
535                 print(">");
536                 for(c = 1; c <= TET_WIDTH; ++c)
537                 {
538                         print(ftos(GetXBlock(c, l)));
539                 }
540                 print("\n");
541         }
542 }
543
544 float BastetEvaluate()
545 {
546         float height;
547         float score;
548         float occupied;
549         float occupied_count;
550         float l, lines;
551         float score_save, occupied_save, occupied_count_save;
552         float i, j, line;
553
554         score = 0;
555
556         // adds a bonus for each free dot above the occupied blocks profile
557         occupied_count = TET_WIDTH;
558         height = 0;
559         lines = 0;
560         for(i = 1; i <= TET_LINES; ++i)
561         {
562                 l = GetLine(i);
563                 if(l == 0)
564                 {
565                         height = i;
566                         continue;
567                 }
568                 line = 1;
569                 occupied_save = occupied;
570                 occupied_count_save = occupied_count;
571                 score_save = score;
572                 for(j = 1; j <= TET_WIDTH; ++j)
573                 {
574                         if(GetXBlock(j, l))
575                         {
576                                 if(!GetXBlock(j, occupied))
577                                 {
578                                         occupied = SetXBlock(j, occupied, 1);
579                                         --occupied_count;
580                                 }
581                         }
582                         else
583                                 line = 0;
584                         score += 10000 * occupied_count;
585                 }
586                 if(line)
587                 {
588                         occupied = occupied_save;
589                         occupied_count = occupied_count_save;
590                         score = score_save + 100000000 + 10000 * TET_WIDTH + 1000;
591                         ++lines;
592                 }
593         }
594
595         score += 1000 * height;
596
597         return score;
598 }
599
600 float CheckMetrics(float piece, float orgx, float orgy, float rot);
601 void ClearPiece(float piece, float orgx, float orgy, float rot);
602 void CementPiece(float piece, float orgx, float orgy, float rot);
603 float bastet_profile_evaluate_time;
604 float BastetSearch(float buf, float pc, float x, float y, float rot, float move_bias)
605 // returns best score, or -1 if position is impossible
606 {
607         string r;
608         float b;
609         float s, sm;
610
611         if(move_bias < 0)
612                 return 0; // DO NOT WANT
613
614         if(x < 1 || x > TET_WIDTH || y < 1 || y > TET_LINES)
615                 return -1; // impossible
616         if(rot < 0) rot = 3;
617         if(rot > 3) rot = 0;
618
619         // did we already try?
620         b = x + (TET_WIDTH+2) * (y + (TET_LINES+2) * rot);
621         r = bufstr_get(buf, b);
622         if(r != "") // already tried
623                 return stof(r);
624
625         bufstr_set(buf, b, "0"); // in case we READ that, not that bad - we already got that value in another branch then anyway
626
627 #define ALWAYS_CHECK_METRICS
628 #ifdef ALWAYS_CHECK_METRICS
629         if(CheckMetrics(pc, x, y, rot))
630 #endif
631         {
632                 // try all moves
633                 sm = 1;
634                 s = BastetSearch(buf, pc, x-1, y, rot, move_bias - 1); if(s > sm) sm = s;
635                 s = BastetSearch(buf, pc, x+1, y, rot, move_bias - 1); if(s > sm) sm = s;
636                 s = BastetSearch(buf, pc, x, y, rot+1, move_bias - 1); if(s > sm) sm = s;
637                 s = BastetSearch(buf, pc, x, y, rot-1, move_bias - 1); if(s > sm) sm = s;
638         
639 #ifndef ALWAYS_CHECK_METRICS
640                 if(CheckMetrics(pc, x, y+1, rot))
641                 {
642 #endif
643                         s = BastetSearch(buf, pc, x, y+1, rot, move_bias + 2); if(s > sm) sm = s;
644 #ifndef ALWAYS_CHECK_METRICS
645                 }
646                 else
647                         s = -1;
648 #endif
649
650                 if(s < 0)
651                 {
652                         //print(sprintf("MAY CEMENT AT: %d %d %d\n", x, y, rot));
653                         // moving down did not work - that means we can fixate the block here
654                         var float t1 = gettime(GETTIME_HIRES);
655
656                         CementPiece(pc, x, y, rot);
657                         s = BastetEvaluate();
658                         ClearPiece(pc, x, y, rot);
659
660                         var float t2 = gettime(GETTIME_HIRES);
661                         bastet_profile_evaluate_time += (t2 - t1);
662
663                         if(s > sm) sm = s;
664                 }
665         }
666 #ifdef ALWAYS_CHECK_METRICS
667         else
668                 sm = -1; // impassible
669 #endif
670
671         bufstr_set(buf, b, ftos(sm));
672
673         return sm;
674 }
675
676 float bastet_piece[7];
677 float bastet_score[7];
678 float bastet_piecetime[7];
679 float BastetPiece()
680 {
681         float b;
682
683         bastet_profile_evaluate_time = 0;
684         var float t1 = gettime(GETTIME_HIRES);
685
686         b = buf_create(); bastet_piece[0] = 1; bastet_score[0] = BastetSearch(b, 1, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[0]; buf_del(b);
687         b = buf_create(); bastet_piece[1] = 2; bastet_score[1] = BastetSearch(b, 2, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[1]; buf_del(b);
688         b = buf_create(); bastet_piece[2] = 3; bastet_score[2] = BastetSearch(b, 3, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[2]; buf_del(b);
689         b = buf_create(); bastet_piece[3] = 4; bastet_score[3] = BastetSearch(b, 4, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[3]; buf_del(b);
690         b = buf_create(); bastet_piece[4] = 5; bastet_score[4] = BastetSearch(b, 5, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[4]; buf_del(b);
691         b = buf_create(); bastet_piece[5] = 6; bastet_score[5] = BastetSearch(b, 6, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[5]; buf_del(b);
692         b = buf_create(); bastet_piece[6] = 7; bastet_score[6] = BastetSearch(b, 7, TET_START_PIECE_POS_x, 1+TET_START_PIECE_POS_y, TET_START_PIECE_POS_y, TET_WIDTH) + 100 * random() + bastet_piecetime[6]; buf_del(b);
693
694         var float t2 = gettime(GETTIME_HIRES);
695         dprint(sprintf("Time taken: %.6f seconds (of this, ev = %.2f%%)\n", t2 - t1, 100 * bastet_profile_evaluate_time / (t2 - t1)));
696
697         // sort
698         float i, j, k, p, s;
699
700 /*
701         for(i = 0; i < 7; ++i)
702         {
703                 print(sprintf("piece %s value = %d\n", substring("OJLIZST", bastet_piece[i]-1, 1), bastet_score[i]));
704         }
705 */
706
707         for(i = 0; i < 7; ++i)
708         {
709                 k = i;
710                 p = bastet_piece[k];
711                 s = bastet_score[k];
712                 for(j = i + 1; j < 7; ++j)
713                 {
714                         if(bastet_score[j] < s)
715                         {
716                                 k = j;
717                                 s = bastet_score[k];
718                                 p = bastet_piece[k];
719                         }
720                 }
721                 if(k != i)
722                 {
723                         bastet_score[k] = bastet_score[i];
724                         bastet_piece[k] = bastet_piece[i];
725                         bastet_score[i] = s;
726                         bastet_piece[i] = p;
727                 }
728         }
729
730         b = random();
731         if(b < 0.8)
732                 j = 0;
733         else if(b < 0.92)
734                 j = 1;
735         else if(b < 0.98)
736                 j = 2;
737         else
738                 j = 3;
739         j = bastet_piece[j];
740
741         for(i = 0; i < 7; ++i)
742         {
743                 if(i == j-1)
744                         bastet_piecetime[i] = 0.2 * bastet_piecetime[i];
745                 else
746                         bastet_piecetime[i] = 1.8 * bastet_piecetime[i] + 1000;
747         }
748
749         return j;
750 }
751
752
753 /*
754 *********************************
755
756 Game Mechanics
757
758 *********************************
759 */
760 .float tet_piece_bucket;
761 float RandomPiece()
762 {
763         float i, j;
764         float p, q;
765         float b;
766         float seen;
767
768         if(self.tet_piece_bucket > 1)
769         {
770                 p = mod(self.tet_piece_bucket, 7);
771                 self.tet_piece_bucket = floor(self.tet_piece_bucket / 7);
772                 return p + 1;
773         }
774         else
775         {
776                 p = floor(random() * 7);
777                 seen = pow(2, p);
778                 b = 1;
779                 for(i = 6; i > 0; --i)
780                 {
781                         q = floor(random() * i);
782                         for(j = 0; j <= q; ++j)
783                                 if(seen & pow(2, j))
784                                         ++q;
785                         if(seen & pow(2, q))
786                                 error("foo 1");
787                         if(q >= 7)
788                                 error("foo 2");
789                         seen |= pow(2, q);
790                         b *= 7;
791                         b += q;
792                 }
793                 self.tet_piece_bucket = b;
794                 return p + 1;
795         }
796 };
797
798 void TetAddScore(float n)
799 {
800         self.tet_score = self.tet_score + n * Tetris_Level();
801         if (self.tet_score > tet_high_score)
802                 tet_high_score = self.tet_score;
803 };
804 float CheckMetrics(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
805 {
806         // check to see if the piece, if moved to the locations will overlap
807
808         float x, y;
809         // why did I start counting from 1, damnit
810         orgx = orgx - 1;
811         orgy = orgy - 1;
812
813         for (y = 0; y < 5; y = y + 1)
814         {
815                 for (x = 0; x < 5; x = x + 1)
816                 {
817                         if (PieceMetric(x, y, rot, piece))
818                         {
819                                 if (GetSquare(x + orgx, y + orgy))
820                                         return FALSE; // uhoh, gonna hit something.
821                                 if (x+orgx<1 || x+orgx > TET_WIDTH || y+orgy<1 || y+orgy> TET_LINES)
822                                         return FALSE; // ouside the level
823                         }
824                 }
825         }
826         return TRUE;
827 }
828
829 void ClearPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
830 {
831
832         float x, y;
833         // why did I start counting from 1, damnit
834         orgx = orgx - 1;
835         orgy = orgy - 1;
836
837         for (y = 0; y < 5; y = y + 1)
838         {
839                 for (x = 0; x < 5; x = x + 1)
840                 {
841                         if (PieceMetric(x, y, rot, piece))
842                         {
843                                 SetSquare(x + orgx, y + orgy, 0);
844                         }
845                 }
846         }
847 }
848 void CementPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
849 {
850         float pcolor;
851         float x, y;
852         // why did I start counting from 1, damnit
853         orgx = orgx - 1;
854         orgy = orgy - 1;
855
856         pcolor = mod(piece, 3) + 1;
857
858         for (y = 0; y < 5; y = y + 1)
859         {
860                 for (x = 0; x < 5; x = x + 1)
861                 {
862                         if (PieceMetric(x, y, rot, piece))
863                         {
864                                 SetSquare(x + orgx, y + orgy, pcolor);
865                         }
866                 }
867         }
868 }
869
870 float LINE_LOW = 349525;
871 float LINE_HIGH = 699050; // above number times 2
872
873 void AddLines(float n)
874 {
875         entity head;
876         if(!self.tet_vs_id)
877                 return;
878         FOR_EACH_REALCLIENT(head) if(head != self) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
879                 head.tet_vs_addlines += n;
880 }
881
882 void CompletedLines()
883 {
884         float y, cleared, ln, added, pos, i;
885
886         cleared = 0;
887         y = TET_LINES;
888         while(y >= 1)
889         {
890                 ln = GetLine(y);
891                 if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)
892                         cleared = cleared + 1;
893                 else
894                         y = y - 1;
895                 ln = GetLine(y - cleared);
896                 SetLine(y, ln);
897         }
898
899         if(cleared >= 4)
900                 AddLines(cleared);
901         else if(cleared >= 1)
902                 AddLines(cleared - 1);
903
904         self.tet_lines = self.tet_lines + cleared;
905         TetAddScore(cleared * cleared * 10);
906
907         added = self.tet_vs_addlines;
908         self.tet_vs_addlines = 0;
909
910         if(added)
911         {
912                 for(y = 1; y <= TET_LINES - added; ++y)
913                 {
914                         SetLine(y, GetLine(y + added));
915                 }
916                 for(y = max(1, TET_LINES - added + 1); y <= TET_LINES; ++y)
917                 {
918                         pos = floor(random() * TET_WIDTH);
919                         ln = 0;
920                         for(i = 1; i <= TET_WIDTH; ++i)
921                                 if(i != pos)
922                                         ln = SetXBlock(i, ln, floor(random() * 3 + 1));
923                         SetLine(y, ln);
924                 }
925         }
926
927         self.tet_highest_line = 0;
928         for(y = 1; y <= TET_LINES; ++y)
929                 if(GetLine(y) != 0)
930                 {
931                         self.tet_highest_line = TET_LINES + 1 - y;
932                         break;
933                 }
934
935         if(added)
936                 tetsnd("tetadd");
937         else if(cleared >= 4)
938                 tetsnd("tetris");
939         else if(cleared)
940                 tetsnd("tetline");
941         else
942                 tetsnd("tetland");
943 };
944
945 void HandleGame(float keyss)
946 {
947
948         // first off, we need to see if we need a new piece
949         vector piece_data;
950         vector check_pos;
951         vector old_pos;
952         float brand_new;
953         float i;
954         brand_new = 0;
955
956
957         if (self.piece_type == 0)
958         {
959                 self.piece_pos = TET_START_PIECE_POS; // that's about middle top, we count from 1 ARGH
960
961                 if(autocvar_g_bastet)
962                 {
963                         self.piece_type = BastetPiece();
964                         self.next_piece = bastet_piece[6];
965                 }
966                 else
967                 {
968                         if (self.next_piece)
969                                 self.piece_type = self.next_piece;
970                         else
971                                 self.piece_type = RandomPiece();
972                         self.next_piece =  RandomPiece();
973                 }
974                 keyss = 0; // no movement first frame
975                 self.tet_autodown = time + 0.2;
976                 brand_new = 1;
977         }
978         else
979                 ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
980
981         // next we need to check the piece metrics against what's on the level
982         // based on the key order
983
984         old_pos = check_pos = self.piece_pos;
985
986         float nudge;
987         nudge = 0;
988         if (keyss & TETKEY_RIGHT)
989         {
990                 check_pos_x = check_pos_x + 1;
991                 tetsnd("tetmove");
992         }
993         else if (keyss & TETKEY_LEFT)
994         {
995                 check_pos_x = check_pos_x - 1;
996                 tetsnd("tetmove");
997         }
998         else if (keyss & TETKEY_ROTRIGHT)
999         {
1000                 check_pos_z = check_pos_z + 1;
1001                 piece_data = PieceShape(self.piece_type);
1002                 nudge = 1;
1003                 tetsnd("tetrot");
1004         }
1005         else if (keyss & TETKEY_ROTLEFT)
1006         {
1007                 check_pos_z = check_pos_z - 1;
1008                 piece_data = PieceShape(self.piece_type);
1009                 nudge = 1;
1010                 tetsnd("tetrot");
1011         }
1012         // bounds check
1013         if (check_pos_z > 3)
1014                 check_pos_z = 0;
1015         else if (check_pos_z < 0)
1016                 check_pos_z = 3;
1017
1018         // reality check
1019         if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
1020                 self.piece_pos = check_pos;
1021         else if (brand_new)
1022         {
1023                 self.tetris_on = 2;
1024                 self.tet_gameovertime = time + 5;
1025                 return;
1026         }
1027         else
1028         {
1029                 for(i = 1; i <= nudge; ++i)
1030                 {
1031                         if(CheckMetrics(self.piece_type, check_pos_x + i, check_pos_y, check_pos_z))
1032                                 self.piece_pos = check_pos + '1 0 0' * i;
1033                         else if(CheckMetrics(self.piece_type, check_pos_x - i, check_pos_y, check_pos_z))
1034                                 self.piece_pos = check_pos - '1 0 0' * i;
1035                         else
1036                                 continue;
1037                         break;
1038                 }
1039         }
1040         check_pos = self.piece_pos;
1041         if(keyss & TETKEY_DROP)
1042         {
1043                 // drop to bottom, but do NOT cement it yet
1044                 // this allows sliding it
1045                 ++check_pos_y;
1046                 while(CheckMetrics(self.piece_type, check_pos_x, check_pos_y + 1, check_pos_z))
1047                         ++check_pos_y;
1048                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
1049         }
1050         else if (keyss & TETKEY_DOWN)
1051         {
1052                 check_pos_y = check_pos_y + 1;
1053                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
1054         }
1055         else if (self.tet_autodown < time)
1056         {
1057                 check_pos_y = check_pos_y + 1;
1058                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
1059         }
1060         if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
1061         {
1062                 if(old_pos != check_pos)
1063                         self.tet_drawtime = 0;
1064                 self.piece_pos = check_pos;
1065         }
1066         else
1067         {
1068                 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
1069                 TetAddScore(1);
1070                 CompletedLines();
1071                 self.piece_type = 0;
1072                 self.tet_drawtime = 0;
1073                 return;
1074         }
1075         CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
1076 };
1077
1078 /*
1079 *********************************
1080
1081 Important Linking Into Quake stuff
1082
1083 *********************************
1084 */
1085
1086
1087 void TetrisImpulse()
1088 {
1089         if(self.tetris_on == 0 || self.tetris_on == 2) // from "off" or "game over"
1090         {
1091                 self.tetris_on = 3;
1092
1093                 if(time < tet_vs_current_timeout)
1094                 {
1095                         // join VS game
1096                         self.tet_vs_id = tet_vs_current_id;
1097                 }
1098                 else
1099                 {
1100                         // start new VS game
1101                         ++tet_vs_current_id;
1102                         tet_vs_current_timeout = time + 15;
1103                         self.tet_vs_id = tet_vs_current_id;
1104                         bprint("^1TET^7R^1IS: ", self.netname, "^1 started a new game. Do ^7impulse 100^1 to join.\n");
1105                 }
1106                 self.tet_highest_line = 0;
1107                 ResetTetris();
1108                 self.tet_org = self.origin;
1109                 self.movetype = MOVETYPE_NOCLIP;
1110         }
1111         else if(self.tetris_on == 1) // from "on"
1112         {
1113                 Tet_GameExit();
1114                 self.impulse = 0;
1115         }
1116 };
1117
1118
1119 float TetrisPreFrame()
1120 {
1121         if (!self.tetris_on)
1122                 return 0;
1123
1124         self.tet_org = self.origin;
1125         if (self.tet_drawtime > time)
1126                 return 1;
1127         Draw_Tetris();
1128         if(self.tetris_on == 3)
1129                 self.tet_drawtime = ceil(time - tet_vs_current_timeout + 0.1) + tet_vs_current_timeout;
1130         else
1131                 self.tet_drawtime = time + 0.5;
1132         return 1;
1133 };
1134 float frik_anglemoda(float v)
1135 {
1136         return v - floor(v/360) * 360;
1137 };
1138 float angcompa(float y1, float y2)
1139 {
1140         y1 = frik_anglemoda(y1);
1141         y2 = frik_anglemoda(y2);
1142
1143         local float answer;
1144         answer = y1 - y2;
1145         if (answer > 180)
1146                 answer = answer - 360;
1147         else if (answer < -180)
1148                 answer = answer + 360;
1149         return answer;
1150 };
1151
1152 .float tetkey_down, tetkey_rotright, tetkey_left, tetkey_right, tetkey_rotleft, tetkey_drop;
1153
1154 float TetrisKeyRepeat(.float fld, float f)
1155 {
1156         if(f)
1157         {
1158                 if(self.fld == 0) // initial key press
1159                 {
1160                         self.fld = time + 0.3;
1161                         return 1;
1162                 }
1163                 else if(time > self.fld)
1164                 {
1165                         self.fld = time + 0.1;
1166                         return 1;
1167                 }
1168                 else
1169                 {
1170                         // repeating too fast
1171                         return 0;
1172                 }
1173         }
1174         else
1175         {
1176                 self.fld = 0;
1177                 return 0;
1178         }
1179 }
1180
1181 float TetrisPostFrame()
1182 {
1183         float keysa;
1184
1185         keysa = 0;
1186
1187         if (!self.tetris_on)
1188                 return 0;
1189
1190         if(self.tetris_on == 2 && time > self.tet_gameovertime)
1191         {
1192                 Tet_GameExit();
1193                 return 0;
1194         }
1195
1196         if(self.tetris_on == 3 && time > tet_vs_current_timeout)
1197         {
1198                 self.tetris_on = 1; // start VS game
1199                 self.tet_drawtime = 0;
1200         }
1201
1202         setorigin(self, self.tet_org);
1203         self.movetype = MOVETYPE_NONE;
1204
1205         if(self.tetris_on == 1)
1206         {
1207                 if(TetrisKeyRepeat(tetkey_down, self.movement_x < 0))
1208                         keysa |= TETKEY_DOWN;
1209
1210                 if(TetrisKeyRepeat(tetkey_rotright, self.movement_x > 0))
1211                         keysa |= TETKEY_ROTRIGHT;
1212
1213                 if(TetrisKeyRepeat(tetkey_left, self.movement_y < 0))
1214                         keysa |= TETKEY_LEFT;
1215
1216                 if(TetrisKeyRepeat(tetkey_right, self.movement_y > 0))
1217                         keysa |= TETKEY_RIGHT;
1218
1219                 if(TetrisKeyRepeat(tetkey_rotleft, self.BUTTON_CROUCH))
1220                         keysa |= TETKEY_ROTLEFT;
1221
1222                 if(TetrisKeyRepeat(tetkey_drop, self.BUTTON_JUMP))
1223                         keysa |= TETKEY_DROP;
1224
1225                 HandleGame(keysa);
1226         }
1227
1228         return 1;
1229 };
1230
1231 #endif