1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2 * This program is public domain.
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 /*************************************************************************/
48 /*************************************************************************/
50 /* The array of piece shapes. It is organized as:
53 * - 4 rotations (ordered clockwise)
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 { "....", "....", "....", "...." } }
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 */
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)
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
]) {
130 piecedata
[i
][r
].shape
[y
][x
] = 0;
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
;
144 piecedata
[i
][r
].shape
[y
][x
] = 0;
145 piecedata
[i
][r
].hot_x
= x
;
146 piecedata
[i
][r
].hot_y
= y
;
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
;
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
);
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",
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])
189 if (piece
< 0 || piece
> 6 || rotation
< 0 || rotation
> 3)
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;
200 /*************************************************************************/
201 /*************************************************************************/
203 /* Return the number of milliseconds of delay between piece drops for the
207 static int level_delay()
209 int level
= levels
[my_playernum
-1];
213 delay
= (delay
*69+35)/70; /* multiply by 69/70 and round */
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];
235 rot
= current_rotation
;
236 pd
= &piecedata
[current_piece
][rot
];
240 for (j
= 0; ok
&& j
< 4; j
++) {
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
]))
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
;
267 for (j
= 0; j
< 4; j
++) {
272 for (i
= 0; i
< 4; i
++) {
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
;
289 for (y
= 0; y
< FIELD_HEIGHT
; y
++) {
291 for (x
= 0; x
< FIELD_WIDTH
; x
++) {
292 if ((*f
)[y
][x
] == 0) {
301 memset(new_specials
, 0, sizeof(new_specials
));
302 for (y
= 0; y
< FIELD_HEIGHT
; y
++) {
304 for (x
= 0; x
< FIELD_WIDTH
; x
++) {
305 if ((*f
)[y
][x
] == 0) {
311 for (x
= 0; x
< FIELD_WIDTH
; x
++) {
313 new_specials
[(*f
)[y
][x
]-6]++;
316 memmove((*f
)[1], (*f
)[0], FIELD_WIDTH
*y
);
317 memset((*f
)[0], 0, FIELD_WIDTH
);
323 while (pos
< special_capacity
&& specials
[pos
] >= 0)
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
);
337 if (pos
< special_capacity
)
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
352 static void place_specials(int num
)
354 Field
*f
= &fields
[my_playernum
-1];
355 int nblocks
= 0, left
;
358 for (y
= 0; y
< FIELD_HEIGHT
; y
++) {
359 for (x
= 0; x
< FIELD_WIDTH
; x
++) {
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)
373 if (rand() % nblocks
< num
) {
374 int which
= 0, n
= rand() % 100;
375 while (n
>= specialfreq
[which
]) {
376 n
-= specialfreq
[which
];
379 (*f
)[y
][x
] = 6 + which
;
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;
402 for (y
= 0; y
< FIELD_HEIGHT
; y
++) {
403 for (x
= 0; x
< FIELD_WIDTH
; x
++) {
404 if ((*f
)[y
][x
] != (*oldfield
)[y
][x
])
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
]) {
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
++) {
437 *s
++ = specials
[(*f
)[y
][x
]-6];
439 *s
++ = (*f
)[y
][x
] + '0';
444 sputs(buf
, server_sock
);
447 /*************************************************************************/
448 /*************************************************************************/
450 /* Generate a new piece and set up the timer. */
457 current_piece
= next_piece
;
460 while (n
>= piecefreq
[next_piece
] && next_piece
< 6) {
461 n
-= piecefreq
[next_piece
];
464 current_rotation
= 0;
465 pd
= &piecedata
[current_piece
][current_rotation
];
467 current_y
= pd
->hot_y
- pd
->top
;
468 if (piece_overlaps(-1, -1, -1)) {
470 if (piece_overlaps(-1, -1, -1)) {
472 if (piece_overlaps(-1, -1, -1)) {
473 Field
*f
= &fields
[my_playernum
-1];
475 for (y
= 0; y
< FIELD_HEIGHT
; y
++) {
476 for (x
= 0; x
< FIELD_WIDTH
; x
++)
477 (*f
)[y
][x
] = rand()%5 + 1;
480 sockprintf(server_sock
, "playerlost %d", my_playernum
);
482 not_playing_game
= 1;
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;
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.
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
;
513 if (y
+1 + pd
->bottom
< FIELD_HEIGHT
&& !piece_overlaps(-1, ynew
, -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;
522 int completed
, level
, nspecials
;
526 memcpy(&oldfield
, f
, sizeof(oldfield
));
528 if (last_special
> lines
) /* i.e. from a previous game */
530 completed
= clear_lines(1);
532 if (old_mode
&& completed
> 1) {
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
;
542 levels
[my_playernum
] = level
;
544 sockprintf(server_sock
, "lvl %d %d", my_playernum
, level
);
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
);
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];
571 io
->draw_attdef(type
, from
, to
);
575 if (to
!= 0 && to
!= my_playernum
&& !(from
==my_playernum
&& *type
=='s'))
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]
589 || strcmp(teams
[my_playernum
-1],teams
[from
-1]) != 0
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
++) {
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
++) {
623 if ((*f
)[y
][x
] == 0) {
624 int y2
, allclear
= 1;
625 for (y2
= y
-1; allclear
&& y2
>= 0; y2
--) {
631 for (y2
= y
-1; y2
>= 0; y2
--)
632 (*f
)[y2
+1][x
] = (*f
)[y2
][x
];
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
)
651 for (y2
= y
-1; y2
<= y
+1; y2
++) {
652 if (y2
< 0 || y2
>= FIELD_HEIGHT
)
654 for (x2
= x
-1; x2
<= x
+1; x2
++) {
655 if (x2
< 0 || x2
>= FIELD_WIDTH
)
657 if (!windows_mode
&& !(*f
)[y2
][x2
])
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
];
675 } else if (*type
== 'q') {
676 for (y
= 0; y
< FIELD_HEIGHT
; y
++) {
677 int r
= rand()%3 - 1;
679 int save
= (*f
)[y
][0];
680 memmove((*f
)[y
], (*f
)[y
]+1, FIELD_WIDTH
-1);
682 (*f
)[y
][FIELD_WIDTH
-1] = 0;
684 (*f
)[y
][FIELD_WIDTH
-1] = save
;
686 int save
= (*f
)[y
][FIELD_WIDTH
-1];
687 memmove((*f
)[y
]+1, (*f
)[y
], FIELD_WIDTH
-1);
695 } else if (*type
== 'r') {
698 for (i
= 0; i
< 10; i
++) {
699 x
= rand() % FIELD_WIDTH
;
700 y
= rand() % FIELD_HEIGHT
;
701 if ((*f
)[y
][x
] != 0) {
707 } else if (*type
== 's') {
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))
729 io
->draw_own_field();
732 /*************************************************************************/
733 /*************************************************************************/
735 /* Deal with the in-game message input buffer. */
737 static char gmsg_buffer
[512];
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)
773 /*************************************************************************/
775 static void gmsg_kill(void)
779 io
->draw_gmsg_input(gmsg_buffer
, gmsg_pos
);
782 /*************************************************************************/
784 static void gmsg_move(int how
)
788 io
->draw_gmsg_input(gmsg_buffer
, gmsg_pos
);
789 } else if (how
== -1 && gmsg_pos
> 0) {
791 io
->draw_gmsg_input(gmsg_buffer
, gmsg_pos
);
792 } else if (how
== 1 && gmsg_buffer
[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)
808 if (strncasecmp(gmsg_buffer
, "/me ", 4) == 0)
809 sockprintf(server_sock
, "gmsg * %s %s", players
[my_playernum
-1], gmsg_buffer
+4);
811 sockprintf(server_sock
, "gmsg <%s> %s", players
[my_playernum
-1], gmsg_buffer
);
814 io
->clear_gmsg_input();
820 /*************************************************************************/
821 /*************************************************************************/
823 /* Set up for a new game. */
829 gettimeofday(&timeout
, NULL
);
830 timeout
.tv_usec
+= 1200000;
831 timeout
.tv_sec
+= timeout
.tv_usec
/ 1000000;
832 timeout
.tv_usec
%= 1000000;
836 while (n
>= piecefreq
[next_piece
] && next_piece
< 6) {
837 n
-= piecefreq
[next_piece
];
842 /*************************************************************************/
844 /* Return the number of milliseconds until we want to do something. */
846 int tetris_timeout(void)
851 gettimeofday(&tv
, NULL
);
852 t
= (timeout
.tv_sec
- tv
.tv_sec
) * 1000
853 + (timeout
.tv_usec
-tv
.tv_usec
) / 1000;
857 /*************************************************************************/
859 /* Do something when we hit a timeout. */
861 void tetris_timeout_action(void)
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
;
882 static int gmsg_active
= 0;
885 if (c
== 8 || c
== 127) /* Backspace or Delete */
887 else if (c
== 4) /* Ctrl-D */
889 else if (c
== 21) /* Ctrl-U */
891 else if (c
== K_LEFT
)
893 else if (c
== K_RIGHT
)
895 else if (c
== 1) /* Ctrl-A */
897 else if (c
== 5) /* Ctrl-E */
899 else if (c
== '\r' || c
== '\n') {
902 } else if (c
== 27) { /* Escape */
903 io
->clear_gmsg_input();
905 } else if (c
>= 1 && c
<= 0xFF)
910 if (c
!= 't' && (!playing_game
|| game_paused
))
914 case K_UP
: /* Rotate clockwise */
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
)
926 if (!piece_overlaps(-1, -1, rnew
)) {
927 current_rotation
= rnew
;
929 io
->draw_own_field();
935 case 'z': /* Rotate counterclockwise */
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
)
946 if (!piece_overlaps(-1, -1, rnew
)) {
947 current_rotation
= rnew
;
949 io
->draw_own_field();
955 case K_LEFT
: /* Move left */
958 if (x
+ pd
->left
> 0) {
960 if (!piece_overlaps(current_x
-1, -1, -1)) {
963 io
->draw_own_field();
970 case K_RIGHT
: /* Move right */
973 if (x
+ pd
->right
< FIELD_WIDTH
-1) {
975 if (!piece_overlaps(current_x
+1, -1, -1)) {
978 io
->draw_own_field();
985 case K_DOWN
: /* Down one space */
991 case ' ': /* Down until the piece hits something */
996 while (y
+ pd
->bottom
< FIELD_HEIGHT
&& !piece_overlaps(-1,ynew
,-1)) {
1001 if (ynew
!= current_y
) {
1004 current_y
++; /* Don't allow sliding */
1012 if (specials
[0] == -1)
1014 if (special_capacity
> 1)
1015 memmove(specials
, specials
+1, special_capacity
-1);
1016 specials
[special_capacity
-1] = -1;
1017 io
->draw_specials();
1031 if (specials
[0] == -1)
1033 sockprintf(server_sock
, "sb %d %c %d",
1034 c
, special_chars
[specials
[0]], my_playernum
);
1035 buf
[0] = special_chars
[specials
[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();
1047 io
->draw_gmsg_input(gmsg_buffer
, gmsg_pos
);
1053 /*************************************************************************/
1055 #endif /* !SERVER_ONLY */
1057 /*************************************************************************/