Added support for pieces casting 'shadow'. This feature is controlled by -shadow...
[tetrinet.git] / tetris.c
blobba005eace3a93acc89c7c001e0a1813cd03d8e24
1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2 * This program is public domain.
4 * Tetris core.
5 */
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"
16 /*************************************************************************/
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 */
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 */
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? */
42 static int last_special; /* Last line for which we added a special */
44 /*************************************************************************/
46 #ifndef SERVER_ONLY
48 /*************************************************************************/
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.
60 * This is all parsed by init_shapes, which should be called at startup.
63 static const char shapes[7][4][4][4] = {
64 { { "##X#", "..X.", "##X#", "..X." },
65 { "....", "..#.", "....", "..#." },
66 { "....", "..#.", "....", "..#." },
67 { "....", "..#.", "....", "..#." } },
69 { { "....", "....", "....", "...." },
70 { ".X#.", ".X#.", ".X#.", ".X#." },
71 { ".##.", ".##.", ".##.", ".##." },
72 { "....", "....", "....", "...." } },
74 { { "....", ".#..", "#...", ".##." },
75 { "#X#.", ".X..", "#X#.", ".X.." },
76 { "..#.", "##..", "....", ".#.." },
77 { "....", "....", "....", "...." } },
79 { { "....", "##..", "..#.", ".#.." },
80 { "#X#.", ".X..", "#X#.", ".X.." },
81 { "#...", ".#..", "....", ".##." },
82 { "....", "....", "....", "...." } },
84 { { "....", ".#..", "....", ".#.." },
85 { "#X..", "#X..", "#X..", "#X.." },
86 { ".##.", "#...", ".##.", "#..." },
87 { "....", "....", "....", "...." } },
89 { { "....", "#...", "....", "#..." },
90 { ".X#.", "#X..", ".X#.", "#X.." },
91 { "##..", ".#..", "##..", ".#.." },
92 { "....", "....", "....", "...." } },
94 { { "....", ".#..", ".#..", ".#.." },
95 { "#X#.", "#X..", "#X#.", ".X#." },
96 { ".#..", ".#..", "....", ".#.." },
97 { "....", "....", "....", "...." } }
100 /* piecedata[piece][rot]; filled in by init_shapes() */
101 PieceData piecedata[7][4];
103 /*************************************************************************/
105 /* Parse the shapes array and fill in the piece data. */
107 void init_shapes(void)
109 int i, x, y, r;
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);
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);
171 /*************************************************************************/
173 /* Retrieve the shape for the given piece and rotation. Return -1 if piece
174 * or rotation is invalid, else 0.
177 int get_shape(int piece, int rotation, char buf[4][4])
179 int x, y;
180 char *shape;
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;
190 return 0;
193 /*************************************************************************/
194 /*************************************************************************/
196 /* Return the number of milliseconds of delay between piece drops for the
197 * current level.
200 static int level_delay()
202 int level = levels[my_playernum-1];
203 int delay = 1000;
205 while (--level)
206 delay = (delay*69+35)/70; /* multiply by 69/70 and round */
207 return delay;
210 /*************************************************************************/
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.
217 static int piece_overlaps(int x, int y, int rot)
219 Field *f = &fields[my_playernum-1];
220 PieceData *pd;
221 int i, j, ok;
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;
242 return !ok;
245 /*************************************************************************/
247 /* Draw the piece in its current position on the board. If draw == 0, then
248 * erase the piece rather than drawing it.
251 static int draw_piece(int draw)
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;
260 for (j = 0; j < 4; j++) {
261 if (y+j < 0) {
262 shape += 4;
263 continue;
265 for (i = 0; i < 4; i++) {
266 if (*shape++)
267 (*f)[y+j][x+i] = c;
272 /*************************************************************************/
274 /* Clear any full lines on the field; return the number of lines cleared. */
276 static int clear_lines(int add_specials)
278 Field *f = &fields[my_playernum-1];
279 int x, y, count = 0, i, j, k;
280 int new_specials[9];
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;
290 if (full)
291 count++;
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;
303 if (full) {
304 for (x = 0; x < FIELD_WIDTH; x++) {
305 if ((*f)[y][x] > 5)
306 new_specials[(*f)[y][x]-6]++;
308 if (y > 0)
309 memmove((*f)[1], (*f)[0], FIELD_WIDTH*y);
310 memset((*f)[0], 0, FIELD_WIDTH);
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;
330 if (pos < special_capacity)
331 specials[pos] = -1;
332 io->draw_specials();
335 return count;
338 /*************************************************************************/
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.
345 static void place_specials(int num)
347 Field *f = &fields[my_playernum-1];
348 int nblocks = 0, left;
349 int x, y, tries;
351 for (y = 0; y < FIELD_HEIGHT; y++) {
352 for (x = 0; x < FIELD_WIDTH; x++) {
353 if ((*f)[y][x])
354 nblocks++;
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++;
372 (*f)[y][x] = 6 + which;
373 left--;
377 tries--;
381 /*************************************************************************/
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.
388 static void send_field(Field *oldfield)
390 Field *f = &fields[my_playernum-1];
391 int i, x, y, diff = 0;
392 char buf[512], *s;
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++;
401 } else {
402 diff = FIELD_WIDTH * FIELD_HEIGHT;
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;
415 *s++ = x + '3';
416 *s++ = y + '3';
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';
436 *s = 0;
437 sputs(buf, server_sock);
440 /*************************************************************************/
441 /*************************************************************************/
443 /* Generate a new piece and set up the timer. */
445 void new_piece(void)
447 int n;
448 PieceData *pd;
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++;
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;
472 send_field(NULL);
473 sockprintf(server_sock, "playerlost %d", my_playernum);
474 playing_game = 0;
475 not_playing_game = 1;
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;
489 /*************************************************************************/
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.
496 void step_down(void)
498 Field *f = &fields[my_playernum-1];
499 PieceData *pd = &piecedata[current_piece][current_rotation];
500 int x = current_x - pd->hot_x;
501 int y = current_y - pd->hot_y;
502 int i, j, ynew, ok;
504 draw_piece(0);
505 ynew = current_y+1;
506 if (y+1 + pd->bottom < FIELD_HEIGHT && !piece_overlaps(-1, ynew, -1)) {
507 current_y++;
508 draw_piece(1);
509 io->draw_own_field();
510 gettimeofday(&timeout, NULL);
511 timeout.tv_usec += level_delay() * 1000;
512 timeout.tv_sec += timeout.tv_usec / 1000000;
513 timeout.tv_usec %= 1000000;
514 } else {
515 int completed, level, nspecials;
516 Field oldfield;
517 char buf[16];
519 memcpy(&oldfield, f, sizeof(oldfield));
520 draw_piece(1);
521 if (last_special > lines) /* i.e. from a previous game */
522 last_special = 0;
523 completed = clear_lines(1);
524 lines += completed;
525 if (old_mode && completed > 1) {
526 if (completed < 4)
527 completed--;
528 sockprintf(server_sock, "sb 0 cs%d %d", completed, my_playernum);
529 sprintf(buf, "cs%d", completed);
530 io->draw_attdef(buf, my_playernum, 0);
532 level = initial_level + (lines / lines_per_level) * level_inc;
533 if (level > 100)
534 level = 100;
535 levels[my_playernum] = level;
536 if (completed > 0) {
537 sockprintf(server_sock, "lvl %d %d", my_playernum, level);
538 io->draw_status();
540 nspecials = (lines - last_special) / special_lines;
541 last_special += nspecials * special_lines;
542 nspecials *= special_count;
543 place_specials(nspecials);
544 io->draw_own_field();
545 send_field(&oldfield);
546 piece_waiting = 1;
547 gettimeofday(&timeout, NULL);
548 timeout.tv_usec += tetrifast ? 0 : 600000;
549 timeout.tv_sec += timeout.tv_usec / 1000000;
550 timeout.tv_usec %= 1000000;
554 /*************************************************************************/
556 /* Do something for a special block. */
558 void do_special(const char *type, int from, int to)
560 Field *f = &fields[my_playernum-1];
561 Field oldfield;
562 int x, y;
564 io->draw_attdef(type, from, to);
566 if (!playing_game)
567 return;
568 if (to != 0 && to != my_playernum && !(from==my_playernum && *type=='s'))
569 return;
571 if (!piece_waiting)
572 draw_piece(0);
574 memcpy(&oldfield, f, sizeof(Field));
576 if (strncmp(type, "cs", 2) == 0) {
577 int nlines = atoi(type+2);
579 /* Don't add lines from a team member */
580 if (!teams[my_playernum-1]
581 || !teams[from-1]
582 || strcmp(teams[my_playernum-1],teams[from-1]) != 0
584 while (nlines--) {
585 memmove((*f)[0], (*f)[1], FIELD_WIDTH*(FIELD_HEIGHT-1));
586 for (x = 0; x < FIELD_WIDTH; x++)
587 (*f)[21][x] = 1 + rand()%5;
588 (*f)[FIELD_HEIGHT-1][rand()%FIELD_WIDTH] = 0;
592 } else if (*type == 'a') {
593 memmove((*f)[0], (*f)[1], FIELD_WIDTH*(FIELD_HEIGHT-1));
594 for (x = 0; x < FIELD_WIDTH; x++)
595 (*f)[21][x] = 1 + rand()%5;
596 (*f)[FIELD_HEIGHT-1][rand()%FIELD_WIDTH] = 0;
597 (*f)[FIELD_HEIGHT-1][rand()%FIELD_WIDTH] = 0;
598 (*f)[FIELD_HEIGHT-1][rand()%FIELD_WIDTH] = 0;
600 } else if (*type == 'b') {
601 for (y = 0; y < FIELD_HEIGHT; y++) {
602 for (x = 0; x < FIELD_WIDTH; x++) {
603 if ((*f)[y][x] > 5)
604 (*f)[y][x] = rand()%5 + 1;
608 } else if (*type == 'c') {
609 memmove((*f)[1], (*f)[0], FIELD_WIDTH*(FIELD_HEIGHT-1));
610 memset((*f)[0], 0, FIELD_WIDTH);
612 } else if (*type == 'g') {
613 for (x = 0; x < FIELD_WIDTH; x++) {
614 y = FIELD_HEIGHT-1;
615 while (y > 0) {
616 if ((*f)[y][x] == 0) {
617 int y2, allclear = 1;
618 for (y2 = y-1; allclear && y2 >= 0; y2--) {
619 if ((*f)[y2][x])
620 allclear = 0;
622 if (allclear)
623 break;
624 for (y2 = y-1; y2 >= 0; y2--)
625 (*f)[y2+1][x] = (*f)[y2][x];
626 (*f)[0][x] = 0;
627 } else
628 y--;
631 clear_lines(0);
633 } else if (*type == 'n') {
634 memset(*f, 0, FIELD_WIDTH*FIELD_HEIGHT);
636 } else if (*type == 'o') {
637 int tries, x2, y2, xnew, ynew;
639 for (y = 0; y < FIELD_HEIGHT; y++) {
640 for (x = 0; x < FIELD_WIDTH; x++) {
641 if ((*f)[y][x] != 6 + SPECIAL_O)
642 continue;
643 (*f)[y][x] = 0;
644 for (y2 = y-1; y2 <= y+1; y2++) {
645 if (y2 < 0 || y2 >= FIELD_HEIGHT)
646 continue;
647 for (x2 = x-1; x2 <= x+1; x2++) {
648 if (x2 < 0 || x2 >= FIELD_WIDTH)
649 continue;
650 if (!windows_mode && !(*f)[y2][x2])
651 continue;
652 tries = 10;
653 while (tries--) {
654 xnew = random() % FIELD_WIDTH;
655 ynew = FIELD_HEIGHT-1 - random()%16;
656 if (windows_mode || !(*f)[ynew][xnew]) {
657 (*f)[ynew][xnew] = (*f)[y2][x2];
658 break;
661 (*f)[y2][x2] = 0;
666 clear_lines(0);
668 } else if (*type == 'q') {
669 for (y = 0; y < FIELD_HEIGHT; y++) {
670 int r = rand()%3 - 1;
671 if (r < 0) {
672 int save = (*f)[y][0];
673 memmove((*f)[y], (*f)[y]+1, FIELD_WIDTH-1);
674 if (windows_mode)
675 (*f)[y][FIELD_WIDTH-1] = 0;
676 else
677 (*f)[y][FIELD_WIDTH-1] = save;
678 } else if (r > 0) {
679 int save = (*f)[y][FIELD_WIDTH-1];
680 memmove((*f)[y]+1, (*f)[y], FIELD_WIDTH-1);
681 if (windows_mode)
682 (*f)[y][0] = 0;
683 else
684 (*f)[y][0] = save;
688 } else if (*type == 'r') {
689 int i, tries;
691 for (i = 0; i < 10; i++) {
692 x = rand() % FIELD_WIDTH;
693 y = rand() % FIELD_HEIGHT;
694 if ((*f)[y][x] != 0) {
695 (*f)[y][x] = 0;
696 break;
700 } else if (*type == 's') {
701 Field temp;
703 memcpy(temp, fields[from-1], sizeof(Field));
704 memcpy(fields[from-1], fields[to-1], sizeof(Field));
705 memcpy(fields[to-1], temp, sizeof(Field));
706 if (from == my_playernum || to == my_playernum)
707 memset(fields[my_playernum-1], 0, 6*FIELD_WIDTH);
708 if (from != my_playernum)
709 io->draw_other_field(from);
710 if (to != my_playernum)
711 io->draw_other_field(to);
715 send_field(&oldfield);
717 if (!piece_waiting) {
718 while (piece_overlaps(-1, -1, -1))
719 current_y--;
720 draw_piece(1);
722 io->draw_own_field();
725 /*************************************************************************/
726 /*************************************************************************/
728 /* Deal with the in-game message input buffer. */
730 static char gmsg_buffer[512];
731 static int gmsg_pos;
733 #define curpos (gmsg_buffer+gmsg_pos)
735 /*************************************************************************/
737 static void gmsg_input(int c)
739 if (gmsg_pos < sizeof(gmsg_buffer) - 1) {
740 memmove(curpos+1, curpos, strlen(curpos)+1);
741 gmsg_buffer[gmsg_pos++] = c;
742 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
746 /*************************************************************************/
748 static void gmsg_delete(void)
750 if (gmsg_buffer[gmsg_pos]) {
751 memmove(curpos, curpos+1, strlen(curpos)-1+1);
752 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
756 /*************************************************************************/
758 static void gmsg_backspace(void)
760 if (gmsg_pos > 0) {
761 gmsg_pos--;
762 gmsg_delete();
766 /*************************************************************************/
768 static void gmsg_kill(void)
770 gmsg_pos = 0;
771 *gmsg_buffer = 0;
772 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
775 /*************************************************************************/
777 static void gmsg_move(int how)
779 if (how == -2) {
780 gmsg_pos = 0;
781 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
782 } else if (how == -1 && gmsg_pos > 0) {
783 gmsg_pos--;
784 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
785 } else if (how == 1 && gmsg_buffer[gmsg_pos]) {
786 gmsg_pos++;
787 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
788 } else if (how == 2) {
789 gmsg_pos = strlen(gmsg_buffer);
790 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
794 /*************************************************************************/
796 static void gmsg_enter(void)
798 char buf[1024];
800 if (*gmsg_buffer) {
801 if (strncasecmp(gmsg_buffer, "/me ", 4) == 0)
802 sockprintf(server_sock, "gmsg * %s %s", players[my_playernum-1], gmsg_buffer+4);
803 else
804 sockprintf(server_sock, "gmsg <%s> %s", players[my_playernum-1], gmsg_buffer);
805 gmsg_pos = 0;
806 *gmsg_buffer = 0;
807 io->clear_gmsg_input();
811 #undef curpos
813 /*************************************************************************/
814 /*************************************************************************/
816 /* Set up for a new game. */
818 void new_game(void)
820 int n;
822 gettimeofday(&timeout, NULL);
823 timeout.tv_usec += 1200000;
824 timeout.tv_sec += timeout.tv_usec / 1000000;
825 timeout.tv_usec %= 1000000;
826 piece_waiting = 1;
827 n = rand() % 100;
828 next_piece = 0;
829 while (n >= piecefreq[next_piece] && next_piece < 6) {
830 n -= piecefreq[next_piece];
831 next_piece++;
835 /*************************************************************************/
837 /* Return the number of milliseconds until we want to do something. */
839 int tetris_timeout(void)
841 struct timeval tv;
842 int t;
844 gettimeofday(&tv, NULL);
845 t = (timeout.tv_sec - tv.tv_sec) * 1000
846 + (timeout.tv_usec-tv.tv_usec) / 1000;
847 return t<0 ? 0 : t;
850 /*************************************************************************/
852 /* Do something when we hit a timeout. */
854 void tetris_timeout_action(void)
856 if (piece_waiting)
857 new_piece();
858 else
859 step_down();
862 /*************************************************************************/
864 /* Do something with a character of input. */
866 static const char special_chars[] = "acnrsbgqo";
868 void tetris_input(int c)
870 Field *f = &fields[my_playernum-1];
871 PieceData *pd = &piecedata[current_piece][current_rotation];
872 int x = current_x - pd->hot_x;
873 int y = current_y - pd->hot_y;
874 int rnew, ynew;
875 static int gmsg_active = 0;
877 if (gmsg_active) {
878 if (c == 8 || c == 127) /* Backspace or Delete */
879 gmsg_backspace();
880 else if (c == 4) /* Ctrl-D */
881 gmsg_delete();
882 else if (c == 21) /* Ctrl-U */
883 gmsg_kill();
884 else if (c == K_LEFT)
885 gmsg_move(-1);
886 else if (c == K_RIGHT)
887 gmsg_move(1);
888 else if (c == 1) /* Ctrl-A */
889 gmsg_move(-2);
890 else if (c == 5) /* Ctrl-E */
891 gmsg_move(2);
892 else if (c == '\r' || c == '\n') {
893 gmsg_enter();
894 gmsg_active = 0;
895 } else if (c == 27) { /* Escape */
896 io->clear_gmsg_input();
897 gmsg_active = 0;
898 } else if (c >= 1 && c <= 0xFF)
899 gmsg_input(c);
900 return;
903 if (c != 't' && (!playing_game || game_paused))
904 return;
906 switch (c) {
907 case K_UP: /* Rotate clockwise */
908 case 'x':
909 if (piece_waiting)
910 break;
911 rnew = (current_rotation+1) % 4;
912 pd = &piecedata[current_piece][current_rotation];
913 x = current_x - pd->hot_x;
914 y = current_y - pd->hot_y;
915 if (x + pd->left < 0 || x + pd->right >= FIELD_WIDTH
916 || y + pd->bottom >= FIELD_HEIGHT)
917 break;
918 draw_piece(0);
919 if (!piece_overlaps(-1, -1, rnew)) {
920 current_rotation = rnew;
921 draw_piece(1);
922 io->draw_own_field();
923 } else {
924 draw_piece(1);
926 break;
928 case 'z': /* Rotate counterclockwise */
929 if (piece_waiting)
930 break;
931 rnew = (current_rotation+3) % 4;
932 pd = &piecedata[current_piece][current_rotation];
933 x = current_x - pd->hot_x;
934 y = current_y - pd->hot_y;
935 if (x + pd->left < 0 || x + pd->right >= FIELD_WIDTH
936 || y + pd->bottom >= FIELD_HEIGHT)
937 break;
938 draw_piece(0);
939 if (!piece_overlaps(-1, -1, rnew)) {
940 current_rotation = rnew;
941 draw_piece(1);
942 io->draw_own_field();
943 } else {
944 draw_piece(1);
946 break;
948 case K_LEFT: /* Move left */
949 if (piece_waiting)
950 break;
951 if (x + pd->left > 0) {
952 draw_piece(0);
953 if (!piece_overlaps(current_x-1, -1, -1)) {
954 current_x--;
955 draw_piece(1);
956 io->draw_own_field();
957 } else {
958 draw_piece(1);
961 break;
963 case K_RIGHT: /* Move right */
964 if (piece_waiting)
965 break;
966 if (x + pd->right < FIELD_WIDTH-1) {
967 draw_piece(0);
968 if (!piece_overlaps(current_x+1, -1, -1)) {
969 current_x++;
970 draw_piece(1);
971 io->draw_own_field();
972 } else {
973 draw_piece(1);
976 break;
978 case K_DOWN: /* Down one space */
979 if (piece_waiting)
980 break;
981 step_down();
982 break;
984 case ' ': /* Down until the piece hits something */
985 if (piece_waiting)
986 break;
987 draw_piece(0);
988 ynew = current_y+1;
989 while (y + pd->bottom < FIELD_HEIGHT && !piece_overlaps(-1,ynew,-1)) {
990 ynew++;
991 y++;
993 ynew--;
994 if (ynew != current_y) {
995 current_y = ynew-1;
996 if (noslide)
997 current_y++; /* Don't allow sliding */
998 step_down();
999 } else {
1000 draw_piece(1);
1002 break;
1004 case 'd':
1005 if (specials[0] == -1)
1006 break;
1007 if (special_capacity > 1)
1008 memmove(specials, specials+1, special_capacity-1);
1009 specials[special_capacity-1] = -1;
1010 io->draw_specials();
1011 break;
1013 case '1':
1014 case '2':
1015 case '3':
1016 case '4':
1017 case '5':
1018 case '6': {
1019 char buf[2];
1021 c -= '0';
1022 if (!players[c-1])
1023 break;
1024 if (specials[0] == -1)
1025 break;
1026 sockprintf(server_sock, "sb %d %c %d",
1027 c, special_chars[specials[0]], my_playernum);
1028 buf[0] = special_chars[specials[0]];
1029 buf[1] = 0;
1030 do_special(buf, my_playernum, c);
1031 if (special_capacity > 1)
1032 memmove(specials, specials+1, special_capacity-1);
1033 specials[special_capacity-1] = -1;
1034 io->draw_specials();
1035 break;
1038 case 't':
1039 gmsg_active = 1;
1040 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
1041 break;
1043 } /* switch (c) */
1046 /*************************************************************************/
1048 #endif /* !SERVER_ONLY */
1050 /*************************************************************************/