]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/g_tetris.qc
first version of bastet in QC ;)
[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 float 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 BastetSearch(float buf, float pc, float x, float y, float rot, float move_bias)
604 // returns best score, or -1 if position is impossible
605 {
606         string r;
607         float b;
608         float s, sm;
609
610         if(move_bias < 0)
611                 return 0; // DO NOT WANT
612
613         if(x < 1 || x > TET_WIDTH || y < 1 || y > TET_LINES)
614                 return -1; // impossible
615         if(rot < 0) rot = 3;
616         if(rot > 3) rot = 0;
617
618         // did we already try?
619         b = x + (TET_WIDTH+2) * (y + (TET_LINES+2) * rot);
620         r = bufstr_get(buf, b);
621         if(r != "") // already tried
622                 return stof(r);
623
624         bufstr_set(buf, b, "0"); // in case we READ that, not that bad - we already got that value in another branch then anyway
625
626         if(CheckMetrics(pc, x, y, rot))
627         {
628                 // try all moves
629                 sm = 1;
630                 s = BastetSearch(buf, pc, x-1, y, rot, move_bias - 1); if(s > sm) sm = s;
631                 s = BastetSearch(buf, pc, x+1, y, rot, move_bias - 1); if(s > sm) sm = s;
632                 s = BastetSearch(buf, pc, x, y, rot+1, move_bias - 1); if(s > sm) sm = s;
633                 s = BastetSearch(buf, pc, x, y, rot-1, move_bias - 1); if(s > sm) sm = s;
634                 s = BastetSearch(buf, pc, x, y+1, rot, move_bias + 2); if(s > sm) sm = s;
635
636                 if(s < 0)
637                 {
638                         //print(sprintf("MAY CEMENT AT: %d %d %d\n", x, y, rot));
639                         // moving down did not work - that means we can fixate the block here
640                         CementPiece(pc, x, y, rot);
641                         s = BastetEvaluate();
642                         ClearPiece(pc, x, y, rot);
643                         if(s > sm) sm = s;
644                 }
645         }
646         else
647                 sm = -1; // impassible
648
649         bufstr_set(buf, b, ftos(sm));
650
651         return sm;
652 }
653
654 float bastet_piece[7];
655 float bastet_score[7];
656 float bastet_piecetime[7];
657 float BastetPiece()
658 {
659         float b;
660
661         var float t1 = gettime(GETTIME_HIRES);
662
663         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);
664         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);
665         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);
666         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);
667         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);
668         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);
669         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);
670
671         var float t2 = gettime(GETTIME_HIRES);
672         dprint(sprintf("Time taken: %.6f seconds\n", t2 - t1));
673
674         // sort
675         float i, j, k, p, s;
676
677 /*
678         for(i = 0; i < 7; ++i)
679         {
680                 print(sprintf("piece %s value = %d\n", substring("OJLIZST", bastet_piece[i]-1, 1), bastet_score[i]));
681         }
682 *
683
684         for(i = 0; i < 7; ++i)
685         {
686                 k = i;
687                 p = bastet_piece[k];
688                 s = bastet_score[k];
689                 for(j = i + 1; j < 7; ++j)
690                 {
691                         if(bastet_score[j] < s)
692                         {
693                                 k = j;
694                                 s = bastet_score[k];
695                                 p = bastet_piece[k];
696                         }
697                 }
698                 if(k != i)
699                 {
700                         bastet_score[k] = bastet_score[i];
701                         bastet_piece[k] = bastet_piece[i];
702                         bastet_score[i] = s;
703                         bastet_piece[i] = p;
704                 }
705         }
706
707         b = random();
708         if(b < 0.8)
709                 j = 0;
710         else if(b < 0.92)
711                 j = 1;
712         else if(b < 0.98)
713                 j = 2;
714         else
715                 j = 3;
716         j = bastet_piece[j];
717
718         for(i = 0; i < 7; ++i)
719         {
720                 if(i == j-1)
721                         bastet_piecetime[i] = 0.2 * bastet_piecetime[i];
722                 else
723                         bastet_piecetime[i] = 1.8 * bastet_piecetime[i] + 1000;
724         }
725
726         return j;
727 }
728
729
730 /*
731 *********************************
732
733 Game Mechanics
734
735 *********************************
736 */
737 .float tet_piece_bucket;
738 float RandomPiece()
739 {
740         float i, j;
741         float p, q;
742         float b;
743         float seen;
744
745         if(self.tet_piece_bucket > 1)
746         {
747                 p = mod(self.tet_piece_bucket, 7);
748                 self.tet_piece_bucket = floor(self.tet_piece_bucket / 7);
749                 return p + 1;
750         }
751         else
752         {
753                 p = floor(random() * 7);
754                 seen = pow(2, p);
755                 b = 1;
756                 for(i = 6; i > 0; --i)
757                 {
758                         q = floor(random() * i);
759                         for(j = 0; j <= q; ++j)
760                                 if(seen & pow(2, j))
761                                         ++q;
762                         if(seen & pow(2, q))
763                                 error("foo 1");
764                         if(q >= 7)
765                                 error("foo 2");
766                         seen |= pow(2, q);
767                         b *= 7;
768                         b += q;
769                 }
770                 self.tet_piece_bucket = b;
771                 return p + 1;
772         }
773 };
774
775 void TetAddScore(float n)
776 {
777         self.tet_score = self.tet_score + n * Tetris_Level();
778         if (self.tet_score > tet_high_score)
779                 tet_high_score = self.tet_score;
780 };
781 float CheckMetrics(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
782 {
783         // check to see if the piece, if moved to the locations will overlap
784
785         float x, y;
786         // why did I start counting from 1, damnit
787         orgx = orgx - 1;
788         orgy = orgy - 1;
789
790         for (y = 0; y < 5; y = y + 1)
791         {
792                 for (x = 0; x < 5; x = x + 1)
793                 {
794                         if (PieceMetric(x, y, rot, piece))
795                         {
796                                 if (GetSquare(x + orgx, y + orgy))
797                                         return FALSE; // uhoh, gonna hit something.
798                                 if (x+orgx<1 || x+orgx > TET_WIDTH || y+orgy<1 || y+orgy> TET_LINES)
799                                         return FALSE; // ouside the level
800                         }
801                 }
802         }
803         return TRUE;
804 }
805
806 void ClearPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
807 {
808
809         float x, y;
810         // why did I start counting from 1, damnit
811         orgx = orgx - 1;
812         orgy = orgy - 1;
813
814         for (y = 0; y < 5; y = y + 1)
815         {
816                 for (x = 0; x < 5; x = x + 1)
817                 {
818                         if (PieceMetric(x, y, rot, piece))
819                         {
820                                 SetSquare(x + orgx, y + orgy, 0);
821                         }
822                 }
823         }
824 }
825 void CementPiece(float piece, float orgx, float orgy, float rot) /*FIXDECL*/
826 {
827         float pcolor;
828         float x, y;
829         // why did I start counting from 1, damnit
830         orgx = orgx - 1;
831         orgy = orgy - 1;
832
833         pcolor = mod(piece, 3) + 1;
834
835         for (y = 0; y < 5; y = y + 1)
836         {
837                 for (x = 0; x < 5; x = x + 1)
838                 {
839                         if (PieceMetric(x, y, rot, piece))
840                         {
841                                 SetSquare(x + orgx, y + orgy, pcolor);
842                         }
843                 }
844         }
845 }
846
847 float LINE_LOW = 349525;
848 float LINE_HIGH = 699050; // above number times 2
849
850 void AddLines(float n)
851 {
852         entity head;
853         if(!self.tet_vs_id)
854                 return;
855         FOR_EACH_REALCLIENT(head) if(head != self) if(head.tetris_on) if(head.tet_vs_id == self.tet_vs_id)
856                 head.tet_vs_addlines += n;
857 }
858
859 void CompletedLines()
860 {
861         float y, cleared, ln, added, pos, i;
862
863         cleared = 0;
864         y = TET_LINES;
865         while(y >= 1)
866         {
867                 ln = GetLine(y);
868                 if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)
869                         cleared = cleared + 1;
870                 else
871                         y = y - 1;
872                 ln = GetLine(y - cleared);
873                 SetLine(y, ln);
874         }
875
876         if(cleared >= 4)
877                 AddLines(cleared);
878         else if(cleared >= 1)
879                 AddLines(cleared - 1);
880
881         self.tet_lines = self.tet_lines + cleared;
882         TetAddScore(cleared * cleared * 10);
883
884         added = self.tet_vs_addlines;
885         self.tet_vs_addlines = 0;
886
887         if(added)
888         {
889                 for(y = 1; y <= TET_LINES - added; ++y)
890                 {
891                         SetLine(y, GetLine(y + added));
892                 }
893                 for(y = max(1, TET_LINES - added + 1); y <= TET_LINES; ++y)
894                 {
895                         pos = floor(random() * TET_WIDTH);
896                         ln = 0;
897                         for(i = 1; i <= TET_WIDTH; ++i)
898                                 if(i != pos)
899                                         ln = SetXBlock(i, ln, floor(random() * 3 + 1));
900                         SetLine(y, ln);
901                 }
902         }
903
904         self.tet_highest_line = 0;
905         for(y = 1; y <= TET_LINES; ++y)
906                 if(GetLine(y) != 0)
907                 {
908                         self.tet_highest_line = TET_LINES + 1 - y;
909                         break;
910                 }
911
912         if(added)
913                 tetsnd("tetadd");
914         else if(cleared >= 4)
915                 tetsnd("tetris");
916         else if(cleared)
917                 tetsnd("tetline");
918         else
919                 tetsnd("tetland");
920 };
921
922 void HandleGame(float keyss)
923 {
924
925         // first off, we need to see if we need a new piece
926         vector piece_data;
927         vector check_pos;
928         vector old_pos;
929         float brand_new;
930         float i;
931         brand_new = 0;
932
933
934         if (self.piece_type == 0)
935         {
936                 self.piece_pos = TET_START_PIECE_POS; // that's about middle top, we count from 1 ARGH
937
938                 if(autocvar_g_bastet)
939                 {
940                         self.piece_type = BastetPiece();
941                         self.next_piece = bastet_piece[6];
942                 }
943                 else
944                 {
945                         if (self.next_piece)
946                                 self.piece_type = self.next_piece;
947                         else
948                                 self.piece_type = RandomPiece();
949                         self.next_piece =  RandomPiece();
950                 }
951                 keyss = 0; // no movement first frame
952                 self.tet_autodown = time + 0.2;
953                 brand_new = 1;
954         }
955         else
956                 ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
957
958         // next we need to check the piece metrics against what's on the level
959         // based on the key order
960
961         old_pos = check_pos = self.piece_pos;
962
963         float nudge;
964         nudge = 0;
965         if (keyss & TETKEY_RIGHT)
966         {
967                 check_pos_x = check_pos_x + 1;
968                 tetsnd("tetmove");
969         }
970         else if (keyss & TETKEY_LEFT)
971         {
972                 check_pos_x = check_pos_x - 1;
973                 tetsnd("tetmove");
974         }
975         else if (keyss & TETKEY_ROTRIGHT)
976         {
977                 check_pos_z = check_pos_z + 1;
978                 piece_data = PieceShape(self.piece_type);
979                 nudge = 1;
980                 tetsnd("tetrot");
981         }
982         else if (keyss & TETKEY_ROTLEFT)
983         {
984                 check_pos_z = check_pos_z - 1;
985                 piece_data = PieceShape(self.piece_type);
986                 nudge = 1;
987                 tetsnd("tetrot");
988         }
989         // bounds check
990         if (check_pos_z > 3)
991                 check_pos_z = 0;
992         else if (check_pos_z < 0)
993                 check_pos_z = 3;
994
995         // reality check
996         if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
997                 self.piece_pos = check_pos;
998         else if (brand_new)
999         {
1000                 self.tetris_on = 2;
1001                 self.tet_gameovertime = time + 5;
1002                 return;
1003         }
1004         else
1005         {
1006                 for(i = 1; i <= nudge; ++i)
1007                 {
1008                         if(CheckMetrics(self.piece_type, check_pos_x + i, check_pos_y, check_pos_z))
1009                                 self.piece_pos = check_pos + '1 0 0' * i;
1010                         else if(CheckMetrics(self.piece_type, check_pos_x - i, check_pos_y, check_pos_z))
1011                                 self.piece_pos = check_pos - '1 0 0' * i;
1012                         else
1013                                 continue;
1014                         break;
1015                 }
1016         }
1017         check_pos = self.piece_pos;
1018         if(keyss & TETKEY_DROP)
1019         {
1020                 // drop to bottom, but do NOT cement it yet
1021                 // this allows sliding it
1022                 ++check_pos_y;
1023                 while(CheckMetrics(self.piece_type, check_pos_x, check_pos_y + 1, check_pos_z))
1024                         ++check_pos_y;
1025                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
1026         }
1027         else if (keyss & TETKEY_DOWN)
1028         {
1029                 check_pos_y = check_pos_y + 1;
1030                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
1031         }
1032         else if (self.tet_autodown < time)
1033         {
1034                 check_pos_y = check_pos_y + 1;
1035                 self.tet_autodown = time + 2 / (1 + Tetris_Level());
1036         }
1037         if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z))
1038         {
1039                 if(old_pos != check_pos)
1040                         self.tet_drawtime = 0;
1041                 self.piece_pos = check_pos;
1042         }
1043         else
1044         {
1045                 CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
1046                 TetAddScore(1);
1047                 CompletedLines();
1048                 self.piece_type = 0;
1049                 self.tet_drawtime = 0;
1050                 return;
1051         }
1052         CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z);
1053 };
1054
1055 /*
1056 *********************************
1057
1058 Important Linking Into Quake stuff
1059
1060 *********************************
1061 */
1062
1063
1064 void TetrisImpulse()
1065 {
1066         if(self.tetris_on == 0 || self.tetris_on == 2) // from "off" or "game over"
1067         {
1068                 self.tetris_on = 3;
1069
1070                 if(time < tet_vs_current_timeout)
1071                 {
1072                         // join VS game
1073                         self.tet_vs_id = tet_vs_current_id;
1074                 }
1075                 else
1076                 {
1077                         // start new VS game
1078                         ++tet_vs_current_id;
1079                         tet_vs_current_timeout = time + 15;
1080                         self.tet_vs_id = tet_vs_current_id;
1081                         bprint("^1TET^7R^1IS: ", self.netname, "^1 started a new game. Do ^7impulse 100^1 to join.\n");
1082                 }
1083                 self.tet_highest_line = 0;
1084                 ResetTetris();
1085                 self.tet_org = self.origin;
1086                 self.movetype = MOVETYPE_NOCLIP;
1087         }
1088         else if(self.tetris_on == 1) // from "on"
1089         {
1090                 Tet_GameExit();
1091                 self.impulse = 0;
1092         }
1093 };
1094
1095
1096 float TetrisPreFrame()
1097 {
1098         if (!self.tetris_on)
1099                 return 0;
1100
1101         self.tet_org = self.origin;
1102         if (self.tet_drawtime > time)
1103                 return 1;
1104         Draw_Tetris();
1105         if(self.tetris_on == 3)
1106                 self.tet_drawtime = ceil(time - tet_vs_current_timeout + 0.1) + tet_vs_current_timeout;
1107         else
1108                 self.tet_drawtime = time + 0.5;
1109         return 1;
1110 };
1111 float frik_anglemoda(float v)
1112 {
1113         return v - floor(v/360) * 360;
1114 };
1115 float angcompa(float y1, float y2)
1116 {
1117         y1 = frik_anglemoda(y1);
1118         y2 = frik_anglemoda(y2);
1119
1120         local float answer;
1121         answer = y1 - y2;
1122         if (answer > 180)
1123                 answer = answer - 360;
1124         else if (answer < -180)
1125                 answer = answer + 360;
1126         return answer;
1127 };
1128
1129 .float tetkey_down, tetkey_rotright, tetkey_left, tetkey_right, tetkey_rotleft, tetkey_drop;
1130
1131 float TetrisKeyRepeat(.float fld, float f)
1132 {
1133         if(f)
1134         {
1135                 if(self.fld == 0) // initial key press
1136                 {
1137                         self.fld = time + 0.3;
1138                         return 1;
1139                 }
1140                 else if(time > self.fld)
1141                 {
1142                         self.fld = time + 0.1;
1143                         return 1;
1144                 }
1145                 else
1146                 {
1147                         // repeating too fast
1148                         return 0;
1149                 }
1150         }
1151         else
1152         {
1153                 self.fld = 0;
1154                 return 0;
1155         }
1156 }
1157
1158 float TetrisPostFrame()
1159 {
1160         float keysa;
1161
1162         keysa = 0;
1163
1164         if (!self.tetris_on)
1165                 return 0;
1166
1167         if(self.tetris_on == 2 && time > self.tet_gameovertime)
1168         {
1169                 Tet_GameExit();
1170                 return 0;
1171         }
1172
1173         if(self.tetris_on == 3 && time > tet_vs_current_timeout)
1174         {
1175                 self.tetris_on = 1; // start VS game
1176                 self.tet_drawtime = 0;
1177         }
1178
1179         setorigin(self, self.tet_org);
1180         self.movetype = MOVETYPE_NONE;
1181
1182         if(self.tetris_on == 1)
1183         {
1184                 if(TetrisKeyRepeat(tetkey_down, self.movement_x < 0))
1185                         keysa |= TETKEY_DOWN;
1186
1187                 if(TetrisKeyRepeat(tetkey_rotright, self.movement_x > 0))
1188                         keysa |= TETKEY_ROTRIGHT;
1189
1190                 if(TetrisKeyRepeat(tetkey_left, self.movement_y < 0))
1191                         keysa |= TETKEY_LEFT;
1192
1193                 if(TetrisKeyRepeat(tetkey_right, self.movement_y > 0))
1194                         keysa |= TETKEY_RIGHT;
1195
1196                 if(TetrisKeyRepeat(tetkey_rotleft, self.BUTTON_CROUCH))
1197                         keysa |= TETKEY_ROTLEFT;
1198
1199                 if(TetrisKeyRepeat(tetkey_drop, self.BUTTON_JUMP))
1200                         keysa |= TETKEY_DROP;
1201
1202                 HandleGame(keysa);
1203         }
1204
1205         return 1;
1206 };
1207
1208 #endif