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