Removed notice about xwin. There's no xwin.c ;-).
[tetrinet/clach04.git] / tetris.c
blob34bfe5ab4d3e2cf55bdd26f4e9386a2aeee4e20e
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 void 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 y = current_y - pd->hot_y;
501 int ynew;
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];
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);
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();
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;
553 /*************************************************************************/
555 /* Do something for a special block. */
557 void do_special(const char *type, int from, int to)
559 Field *f = &fields[my_playernum-1];
560 Field oldfield;
561 int x, y;
563 io->draw_attdef(type, from, to);
565 if (!playing_game)
566 return;
567 if (to != 0 && to != my_playernum && !(from==my_playernum && *type=='s'))
568 return;
570 if (!piece_waiting)
571 draw_piece(0);
573 memcpy(&oldfield, f, sizeof(Field));
575 if (strncmp(type, "cs", 2) == 0) {
576 int nlines = atoi(type+2);
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
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;
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;
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;
607 } else if (*type == 'c') {
608 memmove((*f)[1], (*f)[0], FIELD_WIDTH*(FIELD_HEIGHT-1));
609 memset((*f)[0], 0, FIELD_WIDTH);
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;
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--;
630 clear_lines(0);
632 } else if (*type == 'n') {
633 memset(*f, 0, FIELD_WIDTH*FIELD_HEIGHT);
635 } else if (*type == 'o') {
636 int tries, x2, y2, xnew, ynew;
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;
660 (*f)[y2][x2] = 0;
665 clear_lines(0);
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;
687 } else if (*type == 'r') {
688 int i;
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;
699 } else if (*type == 's') {
700 Field temp;
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);
714 send_field(&oldfield);
716 if (!piece_waiting) {
717 while (piece_overlaps(-1, -1, -1))
718 current_y--;
719 draw_piece(1);
721 io->draw_own_field();
724 /*************************************************************************/
725 /*************************************************************************/
727 /* Deal with the in-game message input buffer. */
729 static char gmsg_buffer[512];
730 static int gmsg_pos;
732 #define curpos (gmsg_buffer+gmsg_pos)
734 /*************************************************************************/
736 static void gmsg_input(int c)
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);
745 /*************************************************************************/
747 static void gmsg_delete(void)
749 if (gmsg_buffer[gmsg_pos]) {
750 memmove(curpos, curpos+1, strlen(curpos)-1+1);
751 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
755 /*************************************************************************/
757 static void gmsg_backspace(void)
759 if (gmsg_pos > 0) {
760 gmsg_pos--;
761 gmsg_delete();
765 /*************************************************************************/
767 static void gmsg_kill(void)
769 gmsg_pos = 0;
770 *gmsg_buffer = 0;
771 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
774 /*************************************************************************/
776 static void gmsg_move(int how)
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);
793 /*************************************************************************/
795 static void gmsg_enter(void)
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();
808 #undef curpos
810 /*************************************************************************/
811 /*************************************************************************/
813 /* Set up for a new game. */
815 void new_game(void)
817 int n;
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++;
832 /*************************************************************************/
834 /* Return the number of milliseconds until we want to do something. */
836 int tetris_timeout(void)
838 struct timeval tv;
839 int t;
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;
847 /*************************************************************************/
849 /* Do something when we hit a timeout. */
851 void tetris_timeout_action(void)
853 if (piece_waiting)
854 new_piece();
855 else
856 step_down();
859 /*************************************************************************/
861 /* Do something with a character of input. */
863 static const char special_chars[] = "acnrsbgqo";
865 void tetris_input(int c)
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;
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;
899 if (c != 't' && (!playing_game || game_paused))
900 return;
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);
922 break;
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);
942 break;
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);
957 break;
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);
972 break;
974 case K_DOWN: /* Down one space */
975 if (piece_waiting)
976 break;
977 step_down();
978 break;
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++;
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);
998 break;
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;
1009 case '1':
1010 case '2':
1011 case '3':
1012 case '4':
1013 case '5':
1014 case '6': {
1015 char buf[2];
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;
1034 case 't':
1035 gmsg_active = 1;
1036 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
1037 break;
1039 } /* switch (c) */
1042 /*************************************************************************/
1044 #endif /* !SERVER_ONLY */
1046 /*************************************************************************/