Tetrifast support.
[tetrinet/clach04.git] / tetris.c
blobd2641fa1924127bb5192cea0c4479738a3248be0
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 static int current_piece; /* Current piece number */
37 static int current_rotation; /* Current rotation value */
38 static int current_x; /* Current X position */
39 static 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 typedef struct {
101 int hot_x, hot_y; /* Hotspot coordinates */
102 int top, left; /* Top-left coordinates relative to hotspot */
103 int bottom, right; /* Bottom-right coordinates relative to hotspot */
104 char shape[4][4]; /* Shape data for the piece */
105 } PieceData;
107 /* piecedata[piece][rot]; filled in by init_shapes() */
108 static PieceData piecedata[7][4];
110 /*************************************************************************/
112 /* Parse the shapes array and fill in the piece data. */
114 void init_shapes(void)
116 int i, x, y, r;
118 for (i = 0; i < 7; i++) {
119 for (r = 0; r < 4; r++) {
120 piecedata[i][r].hot_x = -1;
121 piecedata[i][r].hot_y = -1;
122 piecedata[i][r].top = 3;
123 piecedata[i][r].left = 3;
124 piecedata[i][r].bottom = 0;
125 piecedata[i][r].right = 0;
126 for (y = 0; y < 4; y++) {
127 for (x = 0; x < 4; x++) {
128 switch (shapes[i][y][r][x]) {
129 case '.':
130 piecedata[i][r].shape[y][x] = 0;
131 break;
132 case '#':
133 piecedata[i][r].shape[y][x] = 1;
134 if (piecedata[i][r].top > y)
135 piecedata[i][r].top = y;
136 if (piecedata[i][r].left > x)
137 piecedata[i][r].left = x;
138 if (piecedata[i][r].bottom < y)
139 piecedata[i][r].bottom = y;
140 if (piecedata[i][r].right < x)
141 piecedata[i][r].right = x;
142 break;
143 case 'x':
144 piecedata[i][r].shape[y][x] = 0;
145 piecedata[i][r].hot_x = x;
146 piecedata[i][r].hot_y = y;
147 break;
148 case 'X':
149 piecedata[i][r].shape[y][x] = 1;
150 if (piecedata[i][r].top > y)
151 piecedata[i][r].top = y;
152 if (piecedata[i][r].left > x)
153 piecedata[i][r].left = x;
154 if (piecedata[i][r].bottom < y)
155 piecedata[i][r].bottom = y;
156 if (piecedata[i][r].right < x)
157 piecedata[i][r].right = x;
158 piecedata[i][r].hot_x = x;
159 piecedata[i][r].hot_y = y;
160 break;
161 default :
162 fprintf(stderr, "Piece %d rotation %d: "
163 "weird character `%c' at (%d,%d)\n",
164 i, r, shapes[i][y][r][x], x, y);
165 exit(1);
169 if (piecedata[i][r].hot_x < 0 || piecedata[i][r].hot_y < 0) {
170 fprintf(stderr, "Piece %d rotation %d missing hot spot!\n",
171 i, r);
172 exit(1);
178 /*************************************************************************/
180 /* Retrieve the shape for the given piece and rotation. Return -1 if piece
181 * or rotation is invalid, else 0.
184 int get_shape(int piece, int rotation, char buf[4][4])
186 int x, y;
187 char *shape;
189 if (piece < 0 || piece > 6 || rotation < 0 || rotation > 3)
190 return -1;
191 shape = (char *) piecedata[piece][rotation].shape;
192 for (y = 0; y < 4; y++) {
193 for (x = 0; x < 4; x++) {
194 buf[y][x] = *shape++ ? piece%5 + 1 : 0;
197 return 0;
200 /*************************************************************************/
201 /*************************************************************************/
203 /* Return the number of milliseconds of delay between piece drops for the
204 * current level.
207 static int level_delay()
209 int level = levels[my_playernum-1];
210 int delay = 1000;
212 while (--level)
213 delay = (delay*69+35)/70; /* multiply by 69/70 and round */
214 return delay;
217 /*************************************************************************/
219 /* Return whether the piece in the position given by the x, y, and rot
220 * variables (interpreted the same way as current_*) would overlap any
221 * other blocks in the field. A value of -1 means use the current_* value.
224 static int piece_overlaps(int x, int y, int rot)
226 Field *f = &fields[my_playernum-1];
227 PieceData *pd;
228 int i, j, ok;
230 if (x < 0)
231 x = current_x;
232 if (y < 0)
233 y = current_y;
234 if (rot < 0)
235 rot = current_rotation;
236 pd = &piecedata[current_piece][rot];
237 x -= pd->hot_x;
238 y -= pd->hot_y;
239 ok = 1;
240 for (j = 0; ok && j < 4; j++) {
241 if (y+j < 0)
242 continue;
243 for (i = 0; ok && i < 4; i++) {
244 if (pd->shape[j][i] && (y+j >= FIELD_HEIGHT || x+i < 0
245 || x+i >= FIELD_WIDTH || (*f)[y+j][x+i]))
246 ok = 0;
249 return !ok;
252 /*************************************************************************/
254 /* Draw the piece in its current position on the board. If draw == 0, then
255 * erase the piece rather than drawing it.
258 static int draw_piece(int draw)
260 Field *f = &fields[my_playernum-1];
261 char c = draw ? current_piece % 5 + 1 : 0;
262 int x = current_x - piecedata[current_piece][current_rotation].hot_x;
263 int y = current_y - piecedata[current_piece][current_rotation].hot_y;
264 char *shape = (char *) piecedata[current_piece][current_rotation].shape;
265 int i, j;
267 for (j = 0; j < 4; j++) {
268 if (y+j < 0) {
269 shape += 4;
270 continue;
272 for (i = 0; i < 4; i++) {
273 if (*shape++)
274 (*f)[y+j][x+i] = c;
279 /*************************************************************************/
281 /* Clear any full lines on the field; return the number of lines cleared. */
283 static int clear_lines(int add_specials)
285 Field *f = &fields[my_playernum-1];
286 int x, y, count = 0, i, j, k;
287 int new_specials[9];
289 for (y = 0; y < FIELD_HEIGHT; y++) {
290 int full = 1;
291 for (x = 0; x < FIELD_WIDTH; x++) {
292 if ((*f)[y][x] == 0) {
293 full = 0;
294 break;
297 if (full)
298 count++;
301 memset(new_specials, 0, sizeof(new_specials));
302 for (y = 0; y < FIELD_HEIGHT; y++) {
303 int full = 1;
304 for (x = 0; x < FIELD_WIDTH; x++) {
305 if ((*f)[y][x] == 0) {
306 full = 0;
307 break;
310 if (full) {
311 for (x = 0; x < FIELD_WIDTH; x++) {
312 if ((*f)[y][x] > 5)
313 new_specials[(*f)[y][x]-6]++;
315 if (y > 0)
316 memmove((*f)[1], (*f)[0], FIELD_WIDTH*y);
317 memset((*f)[0], 0, FIELD_WIDTH);
321 if (add_specials) {
322 int pos = 0;
323 while (pos < special_capacity && specials[pos] >= 0)
324 pos++;
325 for (i = 0; i < count && pos < special_capacity; i++) {
326 for (j = 0; j < 9 && pos < special_capacity; j++) {
327 for (k = 0; k < new_specials[j] && pos < special_capacity; k++){
328 if (windows_mode && rand()%2) {
329 memmove(specials+1, specials, pos);
330 specials[0] = j;
331 pos++;
332 } else
333 specials[pos++] = j;
337 if (pos < special_capacity)
338 specials[pos] = -1;
339 io->draw_specials();
342 return count;
345 /*************************************************************************/
347 /* Place the given number of specials on the field. If there aren't enough
348 * blocks to replace, replace all of the blocks and drop the rest of the
349 * specials.
352 static void place_specials(int num)
354 Field *f = &fields[my_playernum-1];
355 int nblocks = 0, left;
356 int x, y, tries;
358 for (y = 0; y < FIELD_HEIGHT; y++) {
359 for (x = 0; x < FIELD_WIDTH; x++) {
360 if ((*f)[y][x])
361 nblocks++;
364 if (num > nblocks)
365 num = nblocks;
366 left = num;
367 tries = 10;
368 while (left > 0 && tries > 0) {
369 for (y = 0; left > 0 && y < FIELD_HEIGHT; y++) {
370 for (x = 0; left > 0 && x < FIELD_WIDTH; x++) {
371 if ((*f)[y][x] > 5 || (*f)[y][x] == 0)
372 continue;
373 if (rand() % nblocks < num) {
374 int which = 0, n = rand() % 100;
375 while (n >= specialfreq[which]) {
376 n -= specialfreq[which];
377 which++;
379 (*f)[y][x] = 6 + which;
380 left--;
384 tries--;
388 /*************************************************************************/
390 /* Send the new field, either as differences from the given old field or
391 * (if more efficient) as a complete field. If oldfield is NULL, always
392 * send the complete field.
395 static void send_field(Field *oldfield)
397 Field *f = &fields[my_playernum-1];
398 int i, x, y, diff = 0;
399 char buf[512], *s;
401 if (oldfield) {
402 for (y = 0; y < FIELD_HEIGHT; y++) {
403 for (x = 0; x < FIELD_WIDTH; x++) {
404 if ((*f)[y][x] != (*oldfield)[y][x])
405 diff++;
408 } else {
409 diff = FIELD_WIDTH * FIELD_HEIGHT;
411 if (diff < (FIELD_WIDTH*FIELD_HEIGHT)/2) {
412 s = buf + sprintf(buf, "f %d ", my_playernum);
413 for (i = 0; i < 15; i++) {
414 int seen = 0; /* Have we seen a difference of this block? */
415 for (y = 0; y < FIELD_HEIGHT; y++) {
416 for (x = 0; x < FIELD_WIDTH; x++) {
417 if ((*f)[y][x] == i && (*f)[y][x] != (*oldfield)[y][x]) {
418 if (!seen) {
419 *s++ = i + '!';
420 seen = 1;
422 *s++ = x + '3';
423 *s++ = y + '3';
425 } /* for x */
426 } /* for y */
427 } /* for i (each tile type) */
428 } /* difference check */
429 /* -4 below is to adjust for "f %d " */
430 if (diff >= (FIELD_WIDTH*FIELD_HEIGHT)/2
431 || strlen(buf)-4 > FIELD_WIDTH*FIELD_HEIGHT) {
432 static const char specials[] = "acnrsbgqo";
433 s = buf + sprintf(buf, "f %d ", my_playernum);
434 for (y = 0; y < FIELD_HEIGHT; y++) {
435 for (x = 0; x < FIELD_WIDTH; x++) {
436 if ((*f)[y][x] > 5)
437 *s++ = specials[(*f)[y][x]-6];
438 else
439 *s++ = (*f)[y][x] + '0';
443 *s = 0;
444 sputs(buf, server_sock);
447 /*************************************************************************/
448 /*************************************************************************/
450 /* Generate a new piece and set up the timer. */
452 void new_piece(void)
454 int n;
455 PieceData *pd;
457 current_piece = next_piece;
458 n = rand() % 100;
459 next_piece = 0;
460 while (n >= piecefreq[next_piece] && next_piece < 6) {
461 n -= piecefreq[next_piece];
462 next_piece++;
464 current_rotation = 0;
465 pd = &piecedata[current_piece][current_rotation];
466 current_x = 6;
467 current_y = pd->hot_y - pd->top;
468 if (piece_overlaps(-1, -1, -1)) {
469 current_x--;
470 if (piece_overlaps(-1, -1, -1)) {
471 current_x += 2;
472 if (piece_overlaps(-1, -1, -1)) {
473 Field *f = &fields[my_playernum-1];
474 int x, y;
475 for (y = 0; y < FIELD_HEIGHT; y++) {
476 for (x = 0; x < FIELD_WIDTH; x++)
477 (*f)[y][x] = rand()%5 + 1;
479 send_field(NULL);
480 sockprintf(server_sock, "playerlost %d", my_playernum);
481 playing_game = 0;
482 not_playing_game = 1;
486 draw_piece(1);
487 io->draw_status();
488 io->draw_own_field();
489 gettimeofday(&timeout, NULL);
490 timeout.tv_usec += level_delay() * 1000;
491 timeout.tv_sec += timeout.tv_usec / 1000000;
492 timeout.tv_usec %= 1000000;
493 piece_waiting = 0;
496 /*************************************************************************/
498 /* Step the current piece down one space. If it's already as far as it can
499 * go, solidify it, check for completed lines, send the new field state,
500 * and start a new piece.
503 void step_down(void)
505 Field *f = &fields[my_playernum-1];
506 PieceData *pd = &piecedata[current_piece][current_rotation];
507 int x = current_x - pd->hot_x;
508 int y = current_y - pd->hot_y;
509 int i, j, ynew, ok;
511 draw_piece(0);
512 ynew = current_y+1;
513 if (y+1 + pd->bottom < FIELD_HEIGHT && !piece_overlaps(-1, ynew, -1)) {
514 current_y++;
515 draw_piece(1);
516 io->draw_own_field();
517 gettimeofday(&timeout, NULL);
518 timeout.tv_usec += level_delay() * 1000;
519 timeout.tv_sec += timeout.tv_usec / 1000000;
520 timeout.tv_usec %= 1000000;
521 } else {
522 int completed, level, nspecials;
523 Field oldfield;
524 char buf[16];
526 memcpy(&oldfield, f, sizeof(oldfield));
527 draw_piece(1);
528 if (last_special > lines) /* i.e. from a previous game */
529 last_special = 0;
530 completed = clear_lines(1);
531 lines += completed;
532 if (old_mode && completed > 1) {
533 if (completed < 4)
534 completed--;
535 sockprintf(server_sock, "sb 0 cs%d %d", completed, my_playernum);
536 sprintf(buf, "cs%d", completed);
537 io->draw_attdef(buf, my_playernum, 0);
539 level = initial_level + (lines / lines_per_level) * level_inc;
540 if (level > 100)
541 level = 100;
542 levels[my_playernum] = level;
543 if (completed > 0) {
544 sockprintf(server_sock, "lvl %d %d", my_playernum, level);
545 io->draw_status();
547 nspecials = (lines - last_special) / special_lines;
548 last_special += nspecials * special_lines;
549 nspecials *= special_count;
550 place_specials(nspecials);
551 io->draw_own_field();
552 send_field(&oldfield);
553 piece_waiting = 1;
554 gettimeofday(&timeout, NULL);
555 timeout.tv_usec += tetrifast ? 0 : 600000;
556 timeout.tv_sec += timeout.tv_usec / 1000000;
557 timeout.tv_usec %= 1000000;
561 /*************************************************************************/
563 /* Do something for a special block. */
565 void do_special(const char *type, int from, int to)
567 Field *f = &fields[my_playernum-1];
568 Field oldfield;
569 int x, y;
571 io->draw_attdef(type, from, to);
573 if (!playing_game)
574 return;
575 if (to != 0 && to != my_playernum && !(from==my_playernum && *type=='s'))
576 return;
578 if (!piece_waiting)
579 draw_piece(0);
581 memcpy(&oldfield, f, sizeof(Field));
583 if (strncmp(type, "cs", 2) == 0) {
584 int nlines = atoi(type+2);
586 /* Don't add lines from a team member */
587 if (!teams[my_playernum-1]
588 || !teams[from-1]
589 || strcmp(teams[my_playernum-1],teams[from-1]) != 0
591 while (nlines--) {
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;
599 } else if (*type == 'a') {
600 memmove((*f)[0], (*f)[1], FIELD_WIDTH*(FIELD_HEIGHT-1));
601 for (x = 0; x < FIELD_WIDTH; x++)
602 (*f)[21][x] = 1 + rand()%5;
603 (*f)[FIELD_HEIGHT-1][rand()%FIELD_WIDTH] = 0;
604 (*f)[FIELD_HEIGHT-1][rand()%FIELD_WIDTH] = 0;
605 (*f)[FIELD_HEIGHT-1][rand()%FIELD_WIDTH] = 0;
607 } else if (*type == 'b') {
608 for (y = 0; y < FIELD_HEIGHT; y++) {
609 for (x = 0; x < FIELD_WIDTH; x++) {
610 if ((*f)[y][x] > 5)
611 (*f)[y][x] = rand()%5 + 1;
615 } else if (*type == 'c') {
616 memmove((*f)[1], (*f)[0], FIELD_WIDTH*(FIELD_HEIGHT-1));
617 memset((*f)[0], 0, FIELD_WIDTH);
619 } else if (*type == 'g') {
620 for (x = 0; x < FIELD_WIDTH; x++) {
621 y = FIELD_HEIGHT-1;
622 while (y > 0) {
623 if ((*f)[y][x] == 0) {
624 int y2, allclear = 1;
625 for (y2 = y-1; allclear && y2 >= 0; y2--) {
626 if ((*f)[y2][x])
627 allclear = 0;
629 if (allclear)
630 break;
631 for (y2 = y-1; y2 >= 0; y2--)
632 (*f)[y2+1][x] = (*f)[y2][x];
633 (*f)[0][x] = 0;
634 } else
635 y--;
638 clear_lines(0);
640 } else if (*type == 'n') {
641 memset(*f, 0, FIELD_WIDTH*FIELD_HEIGHT);
643 } else if (*type == 'o') {
644 int tries, x2, y2, xnew, ynew;
646 for (y = 0; y < FIELD_HEIGHT; y++) {
647 for (x = 0; x < FIELD_WIDTH; x++) {
648 if ((*f)[y][x] != 6 + SPECIAL_O)
649 continue;
650 (*f)[y][x] = 0;
651 for (y2 = y-1; y2 <= y+1; y2++) {
652 if (y2 < 0 || y2 >= FIELD_HEIGHT)
653 continue;
654 for (x2 = x-1; x2 <= x+1; x2++) {
655 if (x2 < 0 || x2 >= FIELD_WIDTH)
656 continue;
657 if (!windows_mode && !(*f)[y2][x2])
658 continue;
659 tries = 10;
660 while (tries--) {
661 xnew = random() % FIELD_WIDTH;
662 ynew = FIELD_HEIGHT-1 - random()%16;
663 if (windows_mode || !(*f)[ynew][xnew]) {
664 (*f)[ynew][xnew] = (*f)[y2][x2];
665 break;
668 (*f)[y2][x2] = 0;
673 clear_lines(0);
675 } else if (*type == 'q') {
676 for (y = 0; y < FIELD_HEIGHT; y++) {
677 int r = rand()%3 - 1;
678 if (r < 0) {
679 int save = (*f)[y][0];
680 memmove((*f)[y], (*f)[y]+1, FIELD_WIDTH-1);
681 if (windows_mode)
682 (*f)[y][FIELD_WIDTH-1] = 0;
683 else
684 (*f)[y][FIELD_WIDTH-1] = save;
685 } else if (r > 0) {
686 int save = (*f)[y][FIELD_WIDTH-1];
687 memmove((*f)[y]+1, (*f)[y], FIELD_WIDTH-1);
688 if (windows_mode)
689 (*f)[y][0] = 0;
690 else
691 (*f)[y][0] = save;
695 } else if (*type == 'r') {
696 int i, tries;
698 for (i = 0; i < 10; i++) {
699 x = rand() % FIELD_WIDTH;
700 y = rand() % FIELD_HEIGHT;
701 if ((*f)[y][x] != 0) {
702 (*f)[y][x] = 0;
703 break;
707 } else if (*type == 's') {
708 Field temp;
710 memcpy(temp, fields[from-1], sizeof(Field));
711 memcpy(fields[from-1], fields[to-1], sizeof(Field));
712 memcpy(fields[to-1], temp, sizeof(Field));
713 if (from == my_playernum || to == my_playernum)
714 memset(fields[my_playernum-1], 0, 6*FIELD_WIDTH);
715 if (from != my_playernum)
716 io->draw_other_field(from);
717 if (to != my_playernum)
718 io->draw_other_field(to);
722 send_field(&oldfield);
724 if (!piece_waiting) {
725 while (piece_overlaps(-1, -1, -1))
726 current_y--;
727 draw_piece(1);
729 io->draw_own_field();
732 /*************************************************************************/
733 /*************************************************************************/
735 /* Deal with the in-game message input buffer. */
737 static char gmsg_buffer[512];
738 static int gmsg_pos;
740 #define curpos (gmsg_buffer+gmsg_pos)
742 /*************************************************************************/
744 static void gmsg_input(int c)
746 if (gmsg_pos < sizeof(gmsg_buffer) - 1) {
747 memmove(curpos+1, curpos, strlen(curpos)+1);
748 gmsg_buffer[gmsg_pos++] = c;
749 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
753 /*************************************************************************/
755 static void gmsg_delete(void)
757 if (gmsg_buffer[gmsg_pos]) {
758 memmove(curpos, curpos+1, strlen(curpos)-1+1);
759 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
763 /*************************************************************************/
765 static void gmsg_backspace(void)
767 if (gmsg_pos > 0) {
768 gmsg_pos--;
769 gmsg_delete();
773 /*************************************************************************/
775 static void gmsg_kill(void)
777 gmsg_pos = 0;
778 *gmsg_buffer = 0;
779 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
782 /*************************************************************************/
784 static void gmsg_move(int how)
786 if (how == -2) {
787 gmsg_pos = 0;
788 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
789 } else if (how == -1 && gmsg_pos > 0) {
790 gmsg_pos--;
791 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
792 } else if (how == 1 && gmsg_buffer[gmsg_pos]) {
793 gmsg_pos++;
794 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
795 } else if (how == 2) {
796 gmsg_pos = strlen(gmsg_buffer);
797 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
801 /*************************************************************************/
803 static void gmsg_enter(void)
805 char buf[1024];
807 if (*gmsg_buffer) {
808 if (strncasecmp(gmsg_buffer, "/me ", 4) == 0)
809 sockprintf(server_sock, "gmsg * %s %s", players[my_playernum-1], gmsg_buffer+4);
810 else
811 sockprintf(server_sock, "gmsg <%s> %s", players[my_playernum-1], gmsg_buffer);
812 gmsg_pos = 0;
813 *gmsg_buffer = 0;
814 io->clear_gmsg_input();
818 #undef curpos
820 /*************************************************************************/
821 /*************************************************************************/
823 /* Set up for a new game. */
825 void new_game(void)
827 int n;
829 gettimeofday(&timeout, NULL);
830 timeout.tv_usec += 1200000;
831 timeout.tv_sec += timeout.tv_usec / 1000000;
832 timeout.tv_usec %= 1000000;
833 piece_waiting = 1;
834 n = rand() % 100;
835 next_piece = 0;
836 while (n >= piecefreq[next_piece] && next_piece < 6) {
837 n -= piecefreq[next_piece];
838 next_piece++;
842 /*************************************************************************/
844 /* Return the number of milliseconds until we want to do something. */
846 int tetris_timeout(void)
848 struct timeval tv;
849 int t;
851 gettimeofday(&tv, NULL);
852 t = (timeout.tv_sec - tv.tv_sec) * 1000
853 + (timeout.tv_usec-tv.tv_usec) / 1000;
854 return t<0 ? 0 : t;
857 /*************************************************************************/
859 /* Do something when we hit a timeout. */
861 void tetris_timeout_action(void)
863 if (piece_waiting)
864 new_piece();
865 else
866 step_down();
869 /*************************************************************************/
871 /* Do something with a character of input. */
873 static const char special_chars[] = "acnrsbgqo";
875 void tetris_input(int c)
877 Field *f = &fields[my_playernum-1];
878 PieceData *pd = &piecedata[current_piece][current_rotation];
879 int x = current_x - pd->hot_x;
880 int y = current_y - pd->hot_y;
881 int rnew, ynew;
882 static int gmsg_active = 0;
884 if (gmsg_active) {
885 if (c == 8 || c == 127) /* Backspace or Delete */
886 gmsg_backspace();
887 else if (c == 4) /* Ctrl-D */
888 gmsg_delete();
889 else if (c == 21) /* Ctrl-U */
890 gmsg_kill();
891 else if (c == K_LEFT)
892 gmsg_move(-1);
893 else if (c == K_RIGHT)
894 gmsg_move(1);
895 else if (c == 1) /* Ctrl-A */
896 gmsg_move(-2);
897 else if (c == 5) /* Ctrl-E */
898 gmsg_move(2);
899 else if (c == '\r' || c == '\n') {
900 gmsg_enter();
901 gmsg_active = 0;
902 } else if (c == 27) { /* Escape */
903 io->clear_gmsg_input();
904 gmsg_active = 0;
905 } else if (c >= 1 && c <= 0xFF)
906 gmsg_input(c);
907 return;
910 if (c != 't' && (!playing_game || game_paused))
911 return;
913 switch (c) {
914 case K_UP: /* Rotate clockwise */
915 case 'x':
916 if (piece_waiting)
917 break;
918 rnew = (current_rotation+1) % 4;
919 pd = &piecedata[current_piece][current_rotation];
920 x = current_x - pd->hot_x;
921 y = current_y - pd->hot_y;
922 if (x + pd->left < 0 || x + pd->right >= FIELD_WIDTH
923 || y + pd->bottom >= FIELD_HEIGHT)
924 break;
925 draw_piece(0);
926 if (!piece_overlaps(-1, -1, rnew)) {
927 current_rotation = rnew;
928 draw_piece(1);
929 io->draw_own_field();
930 } else {
931 draw_piece(1);
933 break;
935 case 'z': /* Rotate counterclockwise */
936 if (piece_waiting)
937 break;
938 rnew = (current_rotation+3) % 4;
939 pd = &piecedata[current_piece][current_rotation];
940 x = current_x - pd->hot_x;
941 y = current_y - pd->hot_y;
942 if (x + pd->left < 0 || x + pd->right >= FIELD_WIDTH
943 || y + pd->bottom >= FIELD_HEIGHT)
944 break;
945 draw_piece(0);
946 if (!piece_overlaps(-1, -1, rnew)) {
947 current_rotation = rnew;
948 draw_piece(1);
949 io->draw_own_field();
950 } else {
951 draw_piece(1);
953 break;
955 case K_LEFT: /* Move left */
956 if (piece_waiting)
957 break;
958 if (x + pd->left > 0) {
959 draw_piece(0);
960 if (!piece_overlaps(current_x-1, -1, -1)) {
961 current_x--;
962 draw_piece(1);
963 io->draw_own_field();
964 } else {
965 draw_piece(1);
968 break;
970 case K_RIGHT: /* Move right */
971 if (piece_waiting)
972 break;
973 if (x + pd->right < FIELD_WIDTH-1) {
974 draw_piece(0);
975 if (!piece_overlaps(current_x+1, -1, -1)) {
976 current_x++;
977 draw_piece(1);
978 io->draw_own_field();
979 } else {
980 draw_piece(1);
983 break;
985 case K_DOWN: /* Down one space */
986 if (piece_waiting)
987 break;
988 step_down();
989 break;
991 case ' ': /* Down until the piece hits something */
992 if (piece_waiting)
993 break;
994 draw_piece(0);
995 ynew = current_y+1;
996 while (y + pd->bottom < FIELD_HEIGHT && !piece_overlaps(-1,ynew,-1)) {
997 ynew++;
998 y++;
1000 ynew--;
1001 if (ynew != current_y) {
1002 current_y = ynew-1;
1003 if (noslide)
1004 current_y++; /* Don't allow sliding */
1005 step_down();
1006 } else {
1007 draw_piece(1);
1009 break;
1011 case 'd':
1012 if (specials[0] == -1)
1013 break;
1014 if (special_capacity > 1)
1015 memmove(specials, specials+1, special_capacity-1);
1016 specials[special_capacity-1] = -1;
1017 io->draw_specials();
1018 break;
1020 case '1':
1021 case '2':
1022 case '3':
1023 case '4':
1024 case '5':
1025 case '6': {
1026 char buf[2];
1028 c -= '0';
1029 if (!players[c-1])
1030 break;
1031 if (specials[0] == -1)
1032 break;
1033 sockprintf(server_sock, "sb %d %c %d",
1034 c, special_chars[specials[0]], my_playernum);
1035 buf[0] = special_chars[specials[0]];
1036 buf[1] = 0;
1037 do_special(buf, my_playernum, c);
1038 if (special_capacity > 1)
1039 memmove(specials, specials+1, special_capacity-1);
1040 specials[special_capacity-1] = -1;
1041 io->draw_specials();
1042 break;
1045 case 't':
1046 gmsg_active = 1;
1047 io->draw_gmsg_input(gmsg_buffer, gmsg_pos);
1048 break;
1050 } /* switch (c) */
1053 /*************************************************************************/
1055 #endif /* !SERVER_ONLY */
1057 /*************************************************************************/