1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2 * This program is public domain.
3 *
4 * Tetris core.
5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/time.h>
11 #include "tetrinet.h"
12 #include "tetris.h"
13 #include "io.h"
14 #include "sockets.h"
15
16 /*************************************************************************/
17
18 int piecefreq[7]; /* Frequency (percentage) for each block type */
19 int specialfreq[9]; /* Frequency for each special type */
20 int old_mode; /* Old mode? (i.e. Gameboy-style) */
21 int initial_level; /* Initial level */
22 int lines_per_level; /* Number of lines per level-up */
23 int level_inc; /* Levels to increase at each level-up */
24 int level_average; /* Average all players' levels */
25 int special_lines; /* Number of lines needed for a special block */
26 int special_count; /* Number of special blocks added each time */
27 int special_capacity; /* Capacity of special block inventory */
28
29 Field fields[6]; /* Current field states */
30 int levels[6]; /* Current levels */
31 int lines; /* Lines completed (by us) */
32 char specials[MAX_SPECIALS] = {-1}; /* Special block inventory */
33 int next_piece; /* Next piece to fall */
34
35 static struct timeval timeout; /* Time of next action */
36 int current_piece; /* Current piece number */
37 int current_rotation; /* Current rotation value */
38 int current_x; /* Current X position */
39 int current_y; /* Current Y position */
40 static int piece_waiting; /* Are we waiting for a new piece to start? */
41
42 static int last_special; /* Last line for which we added a special */
43
44 /*************************************************************************/
45
46 #ifndef SERVER_ONLY
47
48 /*************************************************************************/
49
50 /* The array of piece shapes. It is organized as:
51 * - 7 pieces
52 * - 4 rows
53 * - 4 rotations (ordered clockwise)
54 * - 4 points
55 * A . is an empty point, a # is a full one. An X (upper-case) represents
56 * the "hot-spot" of the piece; this is where the coordinates are fastened
57 * to, and is used to determine the piece's new position after rotation.
58 * If the location for an X empty, use a lowercase letter instead.
59 *
60 * This is all parsed by init_shapes, which should be called at startup.
61 */
62
63 static const char shapes[7][4][4][4] = {
64 { { "##X#", "..X.", "##X#", "..X." },
65 { "....", "..#.", "....", "..#." },
66 { "....", "..#.", "....", "..#." },
67 { "....", "..#.", "....", "..#." } },
68
69 { { "....", "....", "....", "...." },
70 { ".X#.", ".X#.", ".X#.", ".X#." },
71 { ".##.", ".##.", ".##.", ".##." },
72 { "....", "....", "....", "...." } },
73
74 { { "....", ".#..", "#...", ".##." },
75 { "#X#.", ".X..", "#X#.", ".X.." },
76 { "..#.", "##..", "....", ".#.." },
77 { "....", "....", "....", "...." } },
78
79 { { "....", "##..", "..#.", ".#.." },
80 { "#X#.", ".X..", "#X#.", ".X.." },
81 { "#...", ".#..", "....", ".##." },
82 { "....", "....", "....", "...." } },
83
84 { { "....", ".#..", "....", ".#.." },
85 { "#X..", "#X..", "#X..", "#X.." },
86 { ".##.", "#...", ".##.", "#..." },
87 { "....", "....", "....", "...." } },
88
89 { { "....", "#...", "....", "#..." },
90 { ".X#.", "#X..", ".X#.", "#X.." },
91 { "##..", ".#..", "##..", ".#.." },
92 { "....", "....", "....", "...." } },
93
94 { { "....", ".#..", ".#..", ".#.." },
95 { "#X#.", "#X..", "#X#.", ".X#." },
96 { ".#..", ".#..", "....", ".#.." },
97 { "....", "....", "....", "...." } }
98 };
99
100 /* piecedata[piece][rot]; filled in by init_shapes() */
101 PieceData piecedata[7][4];
102
103 /*************************************************************************/
104
105 /* Parse the shapes array and fill in the piece data. */
106
init_shapes(void)107 void init_shapes(void)
108 {
109 int i, x, y, r;
110
111 for (i = 0; i < 7; i++) {
112 for (r = 0; r < 4; r++) {
113 piecedata[i][r].hot_x = -1;
114 piecedata[i][r].hot_y = -1;
115 piecedata[i][r].top = 3;
116 piecedata[i][r].left = 3;
117 piecedata[i][r].bottom = 0;
118 piecedata[i][r].right = 0;
119 for (y = 0; y < 4; y++) {
120 for (x = 0; x < 4; x++) {
121 switch (shapes[i][y][r][x]) {
122 case '.':
123 piecedata[i][r].shape[y][x] = 0;
124 break;
125 case '#':
126 piecedata[i][r].shape[y][x] = 1;
127 if (piecedata[i][r].top > y)
128 piecedata[i][r].top = y;
129 if (piecedata[i][r].left > x)
130 piecedata[i][r].left = x;
131 if (piecedata[i][r].bottom < y)
132 piecedata[i][r].bottom = y;
133 if (piecedata[i][r].right < x)
134 piecedata[i][r].right = x;
135 break;
136 case 'x':
137 piecedata[i][r].shape[y][x] = 0;
138 piecedata[i][r].hot_x = x;
139 piecedata[i][r].hot_y = y;
140 break;
141 case 'X':
142 piecedata[i][r].shape[y][x] = 1;
143 if (piecedata[i][r].top > y)
144 piecedata[i][r].top = y;
145 if (piecedata[i][r].left > x)
146 piecedata[i][r].left = x;
147 if (piecedata[i][r].bottom < y)
148 piecedata[i][r].bottom = y;
149 if (piecedata[i][r].right < x)
150 piecedata[i][r].right = x;
151 piecedata[i][r].hot_x = x;
152 piecedata[i][r].hot_y = y;
153 break;
154 default :
155 fprintf(stderr, "Piece %d rotation %d: "
156 "weird character `%c' at (%d,%d)\n",
157 i, r, shapes[i][y][r][x], x, y);
158 exit(1);
159 }
160 }
161 }
162 if (piecedata[i][r].hot_x < 0 || piecedata[i][r].hot_y < 0) {
163 fprintf(stderr, "Piece %d rotation %d missing hot spot!\n",
164 i, r);
165 exit(1);
166 }
167 }
168 }
169 }
170
171 /*************************************************************************/
172
173 /* Retrieve the shape for the given piece and rotation. Return -1 if piece
174 * or rotation is invalid, else 0.
175 */
176
get_shape(int piece,int rotation,char buf[4][4])177 int get_shape(int piece, int rotation, char buf[4][4])
178 {
179 int x, y;
180 char *shape;
181
182 if (piece < 0 || piece > 6 || rotation < 0 || rotation > 3)
183 return -1;
184 shape = (char *) piecedata[piece][rotation].shape;
185 for (y = 0; y < 4; y++) {
186 for (x = 0; x < 4; x++) {
187 buf[y][x] = *shape++ ? piece%5 + 1 : 0;
188 }
189 }
190 return 0;
191 }
192
193 /*************************************************************************/
194 /*************************************************************************/
195
196 /* Return the number of milliseconds of delay between piece drops for the
197 * current level.
198 */
199
level_delay()200 static int level_delay()
201 {
202 int level = levels[my_playernum-1];
203 int delay = 1000;
204
205 while (--level)
206 delay = (delay*69+35)/70; /* multiply by 69/70 and round */
207 return delay;
208 }
209
210 /*************************************************************************/
211
212 /* Return whether the piece in the position given by the x, y, and rot
213 * variables (interpreted the same way as current_*) would overlap any
214 * other blocks in the field. A value of -1 means use the current_* value.
215 */
216
piece_overlaps(int x,int y,int rot)217 static int piece_overlaps(int x, int y, int rot)
218 {
219 Field *f = &fields[my_playernum-1];
220 PieceData *pd;
221 int i, j, ok;
222
223 if (x < 0)
224 x = current_x;
225 if (y < 0)
226 y = current_y;
227 if (rot < 0)
228 rot = current_rotation;
229 pd = &piecedata[current_piece][rot];
230 x -= pd->hot_x;
231 y -= pd->hot_y;
232 ok = 1;
233 for (j = 0; ok && j < 4; j++) {
234 if (y+j < 0)
235 continue;
236 for (i = 0; ok && i < 4; i++) {
237 if (pd->shape[j][i] && (y+j >= FIELD_HEIGHT || x+i < 0
238 || x+i >= FIELD_WIDTH || (*f)[y+j][x+i]))
239 ok = 0;
240 }
241 }
242 return !ok;
243 }
244
245 /*************************************************************************/
246
247 /* Draw the piece in its current position on the board. If draw == 0, then
248 * erase the piece rather than drawing it.
249 */
250
draw_piece(int draw)251 static void draw_piece(int draw)
252 {
253 Field *f = &fields[my_playernum-1];
254 char c = draw ? current_piece % 5 + 1 : 0;
255 int x = current_x - piecedata[current_piece][current_rotation].hot_x;
256 int y = current_y - piecedata[current_piece][current_rotation].hot_y;
257 char *shape = (char *) piecedata[current_piece][current_rotation].shape;
258 int i, j;
259
260 for (j = 0; j < 4; j++) {
261 if (y+j < 0) {
262 shape += 4;
263 continue;
264 }
265 for (i = 0; i < 4; i++) {
266 if (*shape++)
267 (*f)[y+j][x+i] = c;
268 }
269 }
270 }
271
272 /*************************************************************************/
273
274 /* Clear any full lines on the field; return the number of lines cleared. */
275
clear_lines(int add_specials)276 static int clear_lines(int add_specials)
277 {
278 Field *f = &fields[my_playernum-1];
279 int x, y, count = 0, i, j, k;
280 int new_specials[9];
281
282 for (y = 0; y < FIELD_HEIGHT; y++) {
283 int full = 1;
284 for (x = 0; x < FIELD_WIDTH; x++) {
285 if ((*f)[y][x] == 0) {
286 full = 0;
287 break;
288 }
289 }
290 if (full)
291 count++;
292 }
293
294 memset(new_specials, 0, sizeof(new_specials));
295 for (y = 0; y < FIELD_HEIGHT; y++) {
296 int full = 1;
297 for (x = 0; x < FIELD_WIDTH; x++) {
298 if ((*f)[y][x] == 0) {
299 full = 0;
300 break;
301 }
302 }
303 if (full) {
304 for (x = 0; x < FIELD_WIDTH; x++) {
305 if ((*f)[y][x] > 5)
306 new_specials[(*f)[y][x]-6]++;
307 }
308 if (y > 0)
309 memmove((*f)[1], (*f)[0], FIELD_WIDTH*y);
310 memset((*f)[0], 0, FIELD_WIDTH);
311 }
312 }
313
314 if (add_specials) {
315 int pos = 0;
316 while (pos < special_capacity && specials[pos] >= 0)
317 pos++;
318 for (i = 0; i < count && pos < special_capacity; i++) {
319 for (j = 0; j < 9 && pos < special_capacity; j++) {
320 for (k = 0; k < new_specials[j] && pos < special_capacity; k++){
321 if (windows_mode && rand()%2) {
322 memmove(specials+1, specials, pos);
323 specials[0] = j;
324 pos++;
325 } else
326 specials[pos++] = j;
327 }
328 }
329 }
330 if (pos < special_capacity)
331 specials[pos] = -1;
332 io->draw_specials();
333 }
334
335 return count;
336 }
337
338 /*************************************************************************/
339
340 /* Place the given number of specials on the field. If there aren't enough
341 * blocks to replace, replace all of the blocks and drop the rest of the
342 * specials.
343 */
344
place_specials(int num)345 static void place_specials(int num)
346 {
347 Field *f = &fields[my_playernum-1];
348 int nblocks = 0, left;
349 int x, y, tries;
350
351 for (y = 0; y < FIELD_HEIGHT; y++) {
352 for (x = 0; x < FIELD_WIDTH; x++) {
353 if ((*f)[y][x])
354 nblocks++;
355 }
356 }
357 if (num > nblocks)
358 num = nblocks;
359 left = num;
360 tries = 10;
361 while (left > 0 && tries > 0) {
362 for (y = 0; left > 0 && y < FIELD_HEIGHT; y++) {
363 for (x = 0; left > 0 && x < FIELD_WIDTH; x++) {
364 if ((*f)[y][x] > 5 || (*f)[y][x] == 0)
365 continue;
366 if (rand() % nblocks < num) {
367 int which = 0, n = rand() % 100;
368 while (n >= specialfreq[which]) {
369 n -= specialfreq[which];
370 which++;
371 }
372 (*f)[y][x] = 6 + which;
373 left--;
374 }
375 }
376 }
377 tries--;
378 }
379 }
380
381 /*************************************************************************/
382
383 /* Send the new field, either as differences from the given old field or
384 * (if more efficient) as a complete field. If oldfield is NULL, always
385 * send the complete field.
386 */
387
send_field(Field * oldfield)388 static void send_field(Field *oldfield)
389 {
390 Field *f = &fields[my_playernum-1];
391 int i, x, y, diff = 0;
392 char buf[512], *s;
393
394 if (oldfield) {
395 for (y = 0; y < FIELD_HEIGHT; y++) {
396 for (x = 0; x < FIELD_WIDTH; x++) {
397 if ((*f)[y][x] != (*oldfield)[y][x])
398 diff++;
399 }
400 }
401 } else {
402 diff = FIELD_WIDTH * FIELD_HEIGHT;
403 }
404 if (diff < (FIELD_WIDTH*FIELD_HEIGHT)/2) {
405 s = buf + sprintf(buf, "f %d ", my_playernum);
406 for (i = 0; i < 15; i++) {
407 int seen = 0; /* Have we seen a difference of this block? */
408 for (y = 0; y < FIELD_HEIGHT; y++) {
409 for (x = 0; x < FIELD_WIDTH; x++) {
410 if ((*f)[y][x] == i && (*f)[y][x] != (*oldfield)[y][x]) {
411 if (!seen) {
412 *s++ = i + '!';
413 seen = 1;
414 }
415 *s++ = x + '3';
416 *s++ = y + '3';
417 }
418 } /* for x */
419 } /* for y */
420 } /* for i (each tile type) */
421 } /* difference check */
422 /* -4 below is to adjust for "f %d " */
423 if (diff >= (FIELD_WIDTH*FIELD_HEIGHT)/2
424 || strlen(buf)-4 > FIELD_WIDTH*FIELD_HEIGHT) {
425 static const char specials[] = "acnrsbgqo";
426 s = buf + sprintf(buf, "f %d ", my_playernum);
427 for (y = 0; y < FIELD_HEIGHT; y++) {
428 for (x = 0; x < FIELD_WIDTH; x++) {
429 if ((*f)[y][x] > 5)
430 *s++ = specials[(*f)[y][x]-6];
431 else
432 *s++ = (*f)[y][x] + '0';
433 }
434 }
435 }
436 *s = 0;
437 sputs(buf, server_sock);
438 }
439
440 /*************************************************************************/
441 /*************************************************************************/
442
443 /* Generate a new piece and set up the timer. */
444
new_piece(void)445 void new_piece(void)
446 {
447 int n;
448 PieceData *pd;
449
450 current_piece = next_piece;
451 n = rand() % 100;
452 next_piece = 0;
453 while (n >= piecefreq[next_piece] && next_piece < 6) {
454 n -= piecefreq[next_piece];
455 next_piece++;
456 }
457 current_rotation = 0;
458 pd = &piecedata[current_piece][current_rotation];
459 current_x = 6;
460 current_y = pd->hot_y - pd->top;
461 if (piece_overlaps(-1, -1, -1)) {
462 current_x--;
463 if (piece_overlaps(-1, -1, -1)) {
464 current_x += 2;
465 if (piece_overlaps(-1, -1, -1)) {
466 Field *f = &fields[my_playernum-1];
467 int x, y;
468 for (y = 0; y < FIELD_HEIGHT; y++) {
469 for (x = 0; x < FIELD_WIDTH; x++)
470 (*f)[y][x] = rand()%5 + 1;
471 }
472 send_field(NULL);
473 sockprintf(server_sock, "playerlost %d", my_playernum);
474 playing_game = 0;
475 not_playing_game = 1;
476 }
477 }
478 }
479 draw_piece(1);
480 io->draw_status();
481 io->draw_own_field();
482 gettimeofday(&timeout, NULL);
483 timeout.tv_usec += level_delay() * 1000;
484 timeout.tv_sec += timeout.tv_usec / 1000000;
485 timeout.tv_usec %= 1000000;
486 piece_waiting = 0;
487 }
488
489 /*************************************************************************/
490
491 /* Step the current piece down one space. If it's already as far as it can
492 * go, solidify it, check for completed lines, send the new field state,
493 * and start a new piece.
494 */
495
step_down(void)496 void step_down(void)
497 {
498 Field *f = &fields[my_playernum-1];
499 PieceData *pd = &piecedata[current_piece][current_rotation];
500 int y = current_y - pd->hot_y;
501 int ynew;
502
503 draw_piece(0);
504 ynew = current_y+1;
505 if (y+1 + pd->bottom < FIELD_HEIGHT && !piece_overlaps(-1, ynew, -1)) {
506 current_y++;
507 draw_piece(1);
508 io->draw_own_field();
509 gettimeofday(&timeout, NULL);
510 timeout.tv_usec += level_delay() * 1000;
511 timeout.tv_sec += timeout.tv_usec / 1000000;
512 timeout.tv_usec %= 1000000;
513 } else {
514 int completed, level, nspecials;
515 Field oldfield;
516 char buf[16];
517
518 memcpy(&oldfield, f, sizeof(oldfield));
519 draw_piece(1);
520 if (last_special > lines) /* i.e. from a previous game */
521 last_special = 0;
522 completed = clear_lines(1);
523 lines += completed;
524 if (old_mode && completed > 1) {
525 if (completed < 4)
526 completed--;
527 sockprintf(server_sock, "sb 0 cs%d %d", completed, my_playernum);
528 sprintf(buf, "cs%d", completed);
529 io->draw_attdef(buf, my_playernum, 0);
530 }
531 level = initial_level + (lines / lines_per_level) * level_inc;
532 if (level > 100)
533 level = 100;
534 levels[my_playernum] = level;
535 if (completed > 0) {
536 sockprintf(server_sock, "lvl %d %d", my_playernum, level);
537 io->draw_status();
538 }
539 nspecials = (lines - last_special) / special_lines;
540 last_special += nspecials * special_lines;
541 nspecials *= special_count;
542 place_specials(nspecials);
543 io->draw_own_field();
544 send_field(&oldfield);
545 piece_waiting = 1;
546 gettimeofday(&timeout, NULL);
547 timeout.tv_usec += tetrifast ? 0 : 600000;
548 timeout.tv_sec += timeout.tv_usec / 1000000;
549 timeout.tv_usec %= 1000000;
550 }
551 }
552
553 /*************************************************************************/
554
555 /* Do something for a special block. */
556
do_special(const char * type,int from,int to)557 void do_special(const char *type, int from, int to)
558 {
559 Field *f = &fields[my_playernum-1];
560 Field oldfield;
561 int x, y;
562
563 io->draw_attdef(type, from, to);
564
565 if (!playing_game)
566 return;
567 if (to != 0 && to != my_playernum && !(from==my_playernum && *type=='s'))
568 return;
569
570 if (!piece_waiting)
571 draw_piece(0);
572
573 memcpy(&oldfield, f, sizeof(Field));
574
575 if (strncmp(type, "cs", 2) == 0) {
576 int nlines = atoi(type+2);
577
578 /* Don't add lines from a team member */
579 if (!teams[my_playernum-1]
580 || !teams[from-1]
581 || strcmp(teams[my_playernum-1],teams[from-1]) != 0
582 ) {
583 while (nlines--) {
584 memmove((*f)[0], (*f)[1], FIELD_WIDTH*(FIELD_HEIGHT-1));
585 for (x = 0; x < FIELD_WIDTH; x++)
586 (*f)[21][x] = 1 + rand()%5;
587 (*f)[FIELD_HEIGHT-1][rand()%FIELD_WIDTH] = 0;
588 }
589 }
590
591 } else if (*type == 'a') {
592 memmove((*f)[0], (*f)[1], FIELD_WIDTH*(FIELD_HEIGHT-1));
593 for (x = 0; x < FIELD_WIDTH; x++)
594 (*f)[21][x] = 1 + rand()%5;
595 (*f)[FIELD_HEIGHT-1][rand()%FIELD_WIDTH] = 0;
596 (*f)[FIELD_HEIGHT-1][rand()%FIELD_WIDTH] = 0;
597 (*f)[FIELD_HEIGHT-1][rand()%FIELD_WIDTH] = 0;
598
599 } else if (*type == 'b') {
600 for (y = 0; y < FIELD_HEIGHT; y++) {
601 for (x = 0; x < FIELD_WIDTH; x++) {
602 if ((*f)[y][x] > 5)
603 (*f)[y][x] = rand()%5 + 1;
604 }
605 }
606
607 } else if (*type == 'c') {
608 memmove((*f)[1], (*f)[0], FIELD_WIDTH*(FIELD_HEIGHT-1));
609 memset((*f)[0], 0, FIELD_WIDTH);
610
611 } else if (*type == 'g') {
612 for (x = 0; x < FIELD_WIDTH; x++) {
613 y = FIELD_HEIGHT-1;
614 while (y > 0) {
615 if ((*f)[y][x] == 0) {
616 int y2, allclear = 1;
617 for (y2 = y-1; allclear && y2 >= 0; y2--) {
618 if ((*f)[y2][x])
619 allclear = 0;
620 }
621 if (allclear)
622 break;
623 for (y2 = y-1; y2 >= 0; y2--)
624 (*f)[y2+1][x] = (*f)[y2][x];
625 (*f)[0][x] = 0;
626 } else
627 y--;
628 }
629 }
630 clear_lines(0);
631
632 } else if (*type == 'n') {
633 memset(*f, 0, FIELD_WIDTH*FIELD_HEIGHT);
634
635 } else if (*type == 'o') {
636 int tries, x2, y2, xnew, ynew;
637
638 for (y = 0; y < FIELD_HEIGHT; y++) {
639 for (x = 0; x < FIELD_WIDTH; x++) {
640 if ((*f)[y][x] != 6 + SPECIAL_O)
641 continue;
642 (*f)[y][x] = 0;
643 for (y2 = y-1; y2 <= y+1; y2++) {
644 if (y2 < 0 || y2 >= FIELD_HEIGHT)
645 continue;
646 for (x2 = x-1; x2 <= x+1; x2++) {
647 if (x2 < 0 || x2 >= FIELD_WIDTH)
648 continue;
649 if (!windows_mode && !(*f)[y2][x2])
650 continue;
651 tries = 10;
652 while (tries--) {
653 xnew = random() % FIELD_WIDTH;
654 ynew = FIELD_HEIGHT-1 - random()%16;
655 if (windows_mode || !(*f)[ynew][xnew]) {
656 (*f)[ynew][xnew] = (*f)[y2][x2];
657 break;
658 }
659 }
660 (*f)[y2][x2] = 0;
661 }
662 }
663 }
664 }
665 clear_lines(0);
666
667 } else if (*type == 'q') {
668 for (y = 0; y < FIELD_HEIGHT; y++) {
669 int r = rand()%3 - 1;
670 if (r < 0) {
671 int save = (*f)[y][0];
672 memmove((*f)[y], (*f)[y]+1, FIELD_WIDTH-1);
673 if (windows_mode)
674 (*f)[y][FIELD_WIDTH-1] = 0;
675 else
676 (*f)[y][FIELD_WIDTH-1] = save;
677 } else if (r > 0) {
678 int save = (*f)[y][FIELD_WIDTH-1];
679 memmove((*f)[y]+1, (*f)[y], FIELD_WIDTH-1);
680 if (windows_mode)
681 (*f)[y][0] = 0;
682 else
683 (*f)[y][0] = save;
684 }
685 }
686
687 } else if (*type == 'r') {
688 int i;
689
690 for (i = 0; i < 10; i++) {
691 x = rand() % FIELD_WIDTH;
692 y = rand() % FIELD_HEIGHT;
693 if ((*f)[y][x] != 0) {
694 (*f)[y][x] = 0;
695 break;
696 }
697 }
698
699 } else if (*type == 's') {
700 Field temp;
701
702 memcpy(temp, fields[from-1], sizeof(Field));
703 memcpy(fields[from-1], fields[to-1], sizeof(Field));
704 memcpy(fields[to-1], temp, sizeof(Field));
705 if (from == my_playernum || to == my_playernum)
706 memset(fields[my_playernum-1], 0, 6*FIELD_WIDTH);
707 if (from != my_playernum)
708 io->draw_other_field(from);
709 if (to != my_playernum)
710 io->draw_other_field(to);
711
712 }
713
714 send_field(&oldfield);
715
716 if (!piece_waiting) {
717 while (piece_overlaps(-1, -1, -1))
718 current_y--;
719 draw_piece(1);
720 }
721 io->draw_own_field();
722 }
723
724 /*************************************************************************/
725 /*************************************************************************/
726
727 /* Deal with the in-game message input buffer. */
728
729 static char gmsg_buffer[512];
730 static int gmsg_pos;
731
732 #define curpos (gmsg_buffer+gmsg_pos)
733
734 /*************************************************************************/
735
gmsg_input(int c)736 static void gmsg_input(int c)
737 {
738 if (gmsg_pos < sizeof(gmsg_buffer) - 1) {
739 memmove(curpos+1, curpos, strlen(curpos)+1);
740 gmsg_buffer[gmsg_pos++] = c;
741 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
742 }
743 }
744
745 /*************************************************************************/
746
gmsg_delete(void)747 static void gmsg_delete(void)
748 {
749 if (gmsg_buffer[gmsg_pos]) {
750 memmove(curpos, curpos+1, strlen(curpos)-1+1);
751 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
752 }
753 }
754
755 /*************************************************************************/
756
gmsg_backspace(void)757 static void gmsg_backspace(void)
758 {
759 if (gmsg_pos > 0) {
760 gmsg_pos--;
761 gmsg_delete();
762 }
763 }
764
765 /*************************************************************************/
766
gmsg_kill(void)767 static void gmsg_kill(void)
768 {
769 gmsg_pos = 0;
770 *gmsg_buffer = 0;
771 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
772 }
773
774 /*************************************************************************/
775
gmsg_move(int how)776 static void gmsg_move(int how)
777 {
778 if (how == -2) {
779 gmsg_pos = 0;
780 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
781 } else if (how == -1 && gmsg_pos > 0) {
782 gmsg_pos--;
783 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
784 } else if (how == 1 && gmsg_buffer[gmsg_pos]) {
785 gmsg_pos++;
786 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
787 } else if (how == 2) {
788 gmsg_pos = strlen(gmsg_buffer);
789 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
790 }
791 }
792
793 /*************************************************************************/
794
gmsg_enter(void)795 static void gmsg_enter(void)
796 {
797 if (*gmsg_buffer) {
798 if (strncasecmp(gmsg_buffer, "/me ", 4) == 0)
799 sockprintf(server_sock, "gmsg * %s %s", players[my_playernum-1], gmsg_buffer+4);
800 else
801 sockprintf(server_sock, "gmsg <%s> %s", players[my_playernum-1], gmsg_buffer);
802 gmsg_pos = 0;
803 *gmsg_buffer = 0;
804 io->clear_gmsg_input();
805 }
806 }
807
808 #undef curpos
809
810 /*************************************************************************/
811 /*************************************************************************/
812
813 /* Set up for a new game. */
814
new_game(void)815 void new_game(void)
816 {
817 int n;
818
819 gettimeofday(&timeout, NULL);
820 timeout.tv_usec += 1200000;
821 timeout.tv_sec += timeout.tv_usec / 1000000;
822 timeout.tv_usec %= 1000000;
823 piece_waiting = 1;
824 n = rand() % 100;
825 next_piece = 0;
826 while (n >= piecefreq[next_piece] && next_piece < 6) {
827 n -= piecefreq[next_piece];
828 next_piece++;
829 }
830 }
831
832 /*************************************************************************/
833
834 /* Return the number of milliseconds until we want to do something. */
835
tetris_timeout(void)836 int tetris_timeout(void)
837 {
838 struct timeval tv;
839 int t;
840
841 gettimeofday(&tv, NULL);
842 t = (timeout.tv_sec - tv.tv_sec) * 1000
843 + (timeout.tv_usec-tv.tv_usec) / 1000;
844 return t<0 ? 0 : t;
845 }
846
847 /*************************************************************************/
848
849 /* Do something when we hit a timeout. */
850
tetris_timeout_action(void)851 void tetris_timeout_action(void)
852 {
853 if (piece_waiting)
854 new_piece();
855 else
856 step_down();
857 }
858
859 /*************************************************************************/
860
861 /* Do something with a character of input. */
862
863 static const char special_chars[] = "acnrsbgqo";
864
tetris_input(int c)865 void tetris_input(int c)
866 {
867 PieceData *pd = &piecedata[current_piece][current_rotation];
868 int x = current_x - pd->hot_x;
869 int y = current_y - pd->hot_y;
870 int rnew, ynew;
871 static int gmsg_active = 0;
872
873 if (gmsg_active) {
874 if (c == 8 || c == 127) /* Backspace or Delete */
875 gmsg_backspace();
876 else if (c == 4) /* Ctrl-D */
877 gmsg_delete();
878 else if (c == 21) /* Ctrl-U */
879 gmsg_kill();
880 else if (c == K_LEFT)
881 gmsg_move(-1);
882 else if (c == K_RIGHT)
883 gmsg_move(1);
884 else if (c == 1) /* Ctrl-A */
885 gmsg_move(-2);
886 else if (c == 5) /* Ctrl-E */
887 gmsg_move(2);
888 else if (c == '\r' || c == '\n') {
889 gmsg_enter();
890 gmsg_active = 0;
891 } else if (c == 27) { /* Escape */
892 io->clear_gmsg_input();
893 gmsg_active = 0;
894 } else if (c >= 1 && c <= 0xFF)
895 gmsg_input(c);
896 return;
897 }
898
899 if (c != 't' && (!playing_game || game_paused))
900 return;
901
902 switch (c) {
903 case K_UP: /* Rotate clockwise */
904 case 'x':
905 if (piece_waiting)
906 break;
907 rnew = (current_rotation+1) % 4;
908 pd = &piecedata[current_piece][current_rotation];
909 x = current_x - pd->hot_x;
910 y = current_y - pd->hot_y;
911 if (x + pd->left < 0 || x + pd->right >= FIELD_WIDTH
912 || y + pd->bottom >= FIELD_HEIGHT)
913 break;
914 draw_piece(0);
915 if (!piece_overlaps(-1, -1, rnew)) {
916 current_rotation = rnew;
917 draw_piece(1);
918 io->draw_own_field();
919 } else {
920 draw_piece(1);
921 }
922 break;
923
924 case 'z': /* Rotate counterclockwise */
925 if (piece_waiting)
926 break;
927 rnew = (current_rotation+3) % 4;
928 pd = &piecedata[current_piece][current_rotation];
929 x = current_x - pd->hot_x;
930 y = current_y - pd->hot_y;
931 if (x + pd->left < 0 || x + pd->right >= FIELD_WIDTH
932 || y + pd->bottom >= FIELD_HEIGHT)
933 break;
934 draw_piece(0);
935 if (!piece_overlaps(-1, -1, rnew)) {
936 current_rotation = rnew;
937 draw_piece(1);
938 io->draw_own_field();
939 } else {
940 draw_piece(1);
941 }
942 break;
943
944 case K_LEFT: /* Move left */
945 if (piece_waiting)
946 break;
947 if (x + pd->left > 0) {
948 draw_piece(0);
949 if (!piece_overlaps(current_x-1, -1, -1)) {
950 current_x--;
951 draw_piece(1);
952 io->draw_own_field();
953 } else {
954 draw_piece(1);
955 }
956 }
957 break;
958
959 case K_RIGHT: /* Move right */
960 if (piece_waiting)
961 break;
962 if (x + pd->right < FIELD_WIDTH-1) {
963 draw_piece(0);
964 if (!piece_overlaps(current_x+1, -1, -1)) {
965 current_x++;
966 draw_piece(1);
967 io->draw_own_field();
968 } else {
969 draw_piece(1);
970 }
971 }
972 break;
973
974 case K_DOWN: /* Down one space */
975 if (piece_waiting)
976 break;
977 step_down();
978 break;
979
980 case ' ': /* Down until the piece hits something */
981 if (piece_waiting)
982 break;
983 draw_piece(0);
984 ynew = current_y+1;
985 while (y + pd->bottom < FIELD_HEIGHT && !piece_overlaps(-1,ynew,-1)) {
986 ynew++;
987 y++;
988 }
989 ynew--;
990 if (ynew != current_y) {
991 current_y = ynew-1;
992 if (noslide)
993 current_y++; /* Don't allow sliding */
994 step_down();
995 } else {
996 draw_piece(1);
997 }
998 break;
999
1000 case 'd':
1001 if (specials[0] == -1)
1002 break;
1003 if (special_capacity > 1)
1004 memmove(specials, specials+1, special_capacity-1);
1005 specials[special_capacity-1] = -1;
1006 io->draw_specials();
1007 break;
1008
1009 case '1':
1010 case '2':
1011 case '3':
1012 case '4':
1013 case '5':
1014 case '6': {
1015 char buf[2];
1016
1017 c -= '0';
1018 if (!players[c-1])
1019 break;
1020 if (specials[0] == -1)
1021 break;
1022 sockprintf(server_sock, "sb %d %c %d",
1023 c, special_chars[(int) specials[0]], my_playernum);
1024 buf[0] = special_chars[(int) specials[0]];
1025 buf[1] = 0;
1026 do_special(buf, my_playernum, c);
1027 if (special_capacity > 1)
1028 memmove(specials, specials+1, special_capacity-1);
1029 specials[special_capacity-1] = -1;
1030 io->draw_specials();
1031 break;
1032 }
1033
1034 case 't':
1035 gmsg_active = 1;
1036 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
1037 break;
1038
1039 } /* switch (c) */
1040 }
1041
1042 /*************************************************************************/
1043
1044 #endif /* !SERVER_ONLY */
1045
1046 /*************************************************************************/
1047