1 /*
2 * Alizarin Tetris
3 * The main match event-loop. Code here should relate to the state machine
4 * that keeps track of what to display, accepts as input, send out as output,
5 * etc., as time progresses.
6 *
7 * Copyright 2000, Kiri Wagstaff & Westley Weimer
8 */
9
10 #include <config.h> /* go autoconf! */
11 #include <unistd.h>
12 #include <sys/types.h>
13
14 #if HAVE_SYS_SOCKET_H
15 #include <sys/socket.h>
16 #else
17 #if HAVE_WINSOCK_H
18 #include <winsock.h>
19 #endif
20 #endif
21
22 #include "atris.h"
23 #include "grid.h"
24 #include "piece.h"
25 #include "sound.h"
26 #include "ai.h"
27 #include "options.h"
28
29 #include "ai.pro"
30 #include "display.pro"
31 #include "xflame.pro"
32
33 extern int Score[];
34
35 struct state_struct {
36 int ai;
37 int falling;
38 int fall_speed;
39 int accept_input;
40 int tetris_handling;
41 int limbo;
42 int other_in_limbo;
43 int limbo_sent;
44 int draw;
45 Uint32 collide_time; /* time when your piece merges with the rest */
46 Uint32 next_draw;
47 Uint32 draw_timeout;
48 Uint32 tv_next_fall;
49 int fall_event_interval;
50 Uint32 tv_next_tetris;
51 int tetris_event_interval;
52 Uint32 tv_next_ai_think;
53 Uint32 tv_next_ai_move;
54 int ai_interval;
55 int ready_for_fast;
56 int ready_for_rotate;
57 int seed;
58 play_piece cp, np;
59 void * ai_state;
60 /* these two are used by tetris_event() */
61 int check_result;
62 int num_lines_cleared;
63 } State[2];
64
65 /* one position structure per player */
66 struct pos_struct {
67 int x;
68 int y;
69 int rot;
70 int old_x;
71 int old_y;
72 int old_rot;
73 Command move;
74 } pos[2];
75
76 Grid distract_grid[2];
77
78 /***************************************************************************
79 * paste_on_board()
80 * Places the given piece on the board. Uses row-column (== grid)
81 * coordinates.
82 *********************************************************************PROTO*/
83 void
paste_on_board(play_piece * pp,int col,int row,int rot,Grid * g)84 paste_on_board(play_piece *pp, int col, int row, int rot, Grid *g)
85 {
86 int i,j,c;
87
88 for (j=0;j<pp->base->dim;j++)
89 for (i=0;i<pp->base->dim;i++)
90 if ((c=BITMAP(*pp->base,rot,i,j))) {
91 int t_x = i + col; /* was + (screen_x / cs->w); */
92 int t_y = j + row; /* was + (screen_y / cs->h); */
93 if ((t_x<0 || t_y<0 || t_x>=g->w || t_y>=g->h)) {
94 Debug("Serious consistency failure: dropping pieces.\n");
95 continue;
96 }
97 GRID_SET(*g,t_x,t_y,pp->colormap[c]);
98 FALL_SET(*g,t_x,t_y,NOT_FALLING);
99 if (t_x > 0) GRID_CHANGED(*g,t_x-1,t_y) = 1;
100 if (t_y > 0) GRID_CHANGED(*g,t_x,t_y-1) = 1;
101 if (t_x < g->w-1) GRID_CHANGED(*g,t_x+1,t_y) = 1;
102 if (t_y < g->h-1) GRID_CHANGED(*g,t_x,t_y+1) = 1;
103 }
104 return;
105 }
106
107 /***************************************************************************
108 * screen_to_exact_grid_coords()
109 * Converts screen coordinates to exact grid coordinates. Will abort the
110 * program if you pass in unaligned coordinates!
111 ***************************************************************************/
112 static void
screen_to_exact_grid_coords(Grid * g,int blockWidth,int screen_x,int screen_y,int * row,int * col)113 screen_to_exact_grid_coords(Grid *g, int blockWidth,
114 int screen_x, int screen_y, int *row, int *col)
115 {
116 screen_x -= g->board.x;
117 screen_y -= g->board.y;
118
119 Assert(screen_x % blockWidth == 0);
120 Assert(screen_y % blockWidth == 0);
121
122 *row = screen_y / blockWidth;
123 *col = screen_x / blockWidth;
124
125 return;
126 }
127
128 /***************************************************************************
129 * screen_to_grid_coords()
130 * Converts screen coordinates to grid coordinates. Rounds "down".
131 ***************************************************************************/
132 void
screen_to_grid_coords(Grid * g,int blockWidth,int screen_x,int screen_y,int * row,int * col)133 screen_to_grid_coords(Grid *g, int blockWidth,
134 int screen_x, int screen_y, int *row, int *col)
135 {
136 screen_x -= g->board.x;
137 screen_y -= g->board.y;
138 if (screen_x < 0) screen_x -= 19; /* round up to negative #s */
139 if (screen_y < 0) screen_y -= 19; /* round up to negative #s */
140
141 *row = screen_y / blockWidth;
142 *col = screen_x / blockWidth;
143
144 return;
145 }
146
147 /***************************************************************************
148 * valid_position()
149 * Determines if the given position is valid. Uses row-column (== grid)
150 * coordinates. Returns 0 if the piece would fall out of bounds or if
151 * some solid part of the piece would fall over something already on the
152 * the grid. Returns 1 otherwise (it is then safe to call
153 * paste_on_board()).
154 *********************************************************************PROTO*/
155 int
valid_position(play_piece * pp,int col,int row,int rot,Grid * g)156 valid_position(play_piece *pp, int col, int row, int rot, Grid *g)
157 {
158 int i,j;
159
160 /*
161 * We don't want this check because you can have col=-2 or whatnot if
162 * your piece doesn't start on its leftmost border.
163 *
164 * if (col < 0 || col >= g->w || row < 0 || row >= g->h)
165 * return 0;
166 */
167
168 for (j=0;j<pp->base->dim;j++)
169 for (i=0;i<pp->base->dim;i++)
170 if (BITMAP(*pp->base,rot,i,j)) {
171 int t_x = i + col; /* was + (screen_x / cs->w); */
172 int t_y = j + row; /* was + (screen_y / cs->h); */
173 if (t_x < 0 || t_y < 0 || t_x >= g->w || t_y >= g->h)
174 return 0;
175 if (GRID_CONTENT(*g,t_x,t_y)) return 0;
176 }
177 return 1;
178 }
179
180 /***************************************************************************
181 * valid_screen_position()
182 * Determines if the given position is valid. Uses screen coordinates.
183 * Handles pieces that are not perfectly row-aligned. Pieces must still
184 * be perfectly column aligned.
185 *
186 * Returns 0 if the piece would fall out of bounds or if some solid part of
187 * the piece would fall over something already on the the grid. Returns 1
188 * otherwise (it is then safe to call paste_on_board()).
189 *********************************************************************PROTO*/
190 int
valid_screen_position(play_piece * pp,int blockWidth,Grid * g,int rot,int screen_x,int screen_y)191 valid_screen_position(play_piece *pp, int blockWidth, Grid *g,
192 int rot, int screen_x, int screen_y)
193 {
194 int row, row2, col;
195
196 screen_to_grid_coords(g, blockWidth, screen_x, screen_y, &row, &col);
197
198 if (!valid_position(pp, col, row, rot, g))
199 return 0;
200
201 screen_to_grid_coords(g, blockWidth, screen_x, screen_y+19, &row2, &col);
202
203 if (row == row2) return 1; /* no need to recheck, you were aligned */
204 else return valid_position(pp, col, row2, rot, g);
205 }
206
207 /***************************************************************************
208 * tetris_event()
209 * Do some work associated with a collision. Needs the color style to draw
210 * the grid.
211 *************************************************************************/
tetris_event(int * delay,int count,SDL_Surface * screen,piece_style * ps,color_style * cs,sound_style * ss,Grid g[],int level,int fall_event_interval,int sock,int draw,int * blank,int * garbage,int P)212 int tetris_event(int *delay, int count, SDL_Surface * screen,
213 piece_style *ps, color_style *cs, sound_style *ss, Grid g[], int
214 level, int fall_event_interval, int sock, int draw,
215 int *blank, int *garbage, int P)
216 {
217 if (count == 1) { /* determine if anything happened, sounds */
218 int i;
219
220 State[P].check_result = check_tetris(g);
221 State[P].num_lines_cleared += State[P].check_result;
222
223 if (State[P].check_result >= 3)
224 play_sound(ss,SOUND_CLEAR4,256);
225 else for (i=0;i<State[P].check_result;i++)
226 play_sound(ss,SOUND_CLEAR1,256+6144*i);
227
228 *delay = 1;
229 return 2;
230
231 } else if (count == 2) { /* run gravity */
232 int x,y;
233
234 if (sock) {
235 char msg = 'c'; /* WRW: send update */
236 send(sock,&msg,1,0);
237 send(sock,g[0].contents,sizeof(*g[0].contents)
238 * g[0].h * g[0].w,0);
239
240 }
241 draw_grid(screen,cs,&g[0],draw);
242
243 /*
244 * recalculate falling, run gravity
245 */
246 memcpy(g->temp,g->fall,g->w*g->h*sizeof(*(g->temp)));
247 for (y=g->h-1;y>=0;y--)
248 for (x=g->w-1;x>=0;x--)
249 FALL_SET(*g,x,y,UNKNOWN);
250 run_gravity(&g[0]);
251 memset(g->changed,0,g->h*g->w*sizeof(*(g->changed)));
252 for (y=g->h-1;y>=0;y--)
253 for (x=g->w-1;x>=0;x--)
254 if (TEMP_CONTENT(*g,x,y)!=FALL_CONTENT(*g,x,y)){
255 GRID_CHANGED(*g,x,y) = 1;
256 if (x > 0) GRID_CHANGED(*g,x-1,y) = 1;
257 if (y > 0) GRID_CHANGED(*g,x,y-1) = 1;
258 if (x < g->w-1) GRID_CHANGED(*g,x+1,y) = 1;
259 if (y < g->h-1) GRID_CHANGED(*g,x,y+1) = 1;
260 }
261 /*
262 * check: did FALL_CONTENT change?
263 */
264 draw_grid(screen,cs,&g[0],draw);
265
266 if (determine_falling(&g[0])) {
267 *delay = 1;
268 return 3;
269 } else {
270 Score[P] += State[P].num_lines_cleared *
271 State[P].num_lines_cleared * level;
272 if (sock) {
273 char msg = 's'; /* WRW: send update */
274 send(sock,&msg,1,0);
275 send(sock,(char *)&Score[P],sizeof(Score[P]),0);
276 if (State[P].num_lines_cleared >= 5) {
277 char msg = 'g'; /* WRW: send garbage! */
278 send(sock,&msg,1,0);
279 State[P].num_lines_cleared -= 4; /* might possibly also blank! */
280 }
281 if (State[P].num_lines_cleared >= 3) {
282 int i;
283 for (i=3;i<=State[P].num_lines_cleared;i++) {
284 char msg = 'b'; /* WRW: send blanking! */
285 send(sock,&msg,1,0);
286 }
287 }
288 } else {
289 if (State[P].num_lines_cleared >= 5) {
290 *garbage = 1;
291 State[P].num_lines_cleared -= 4;
292 }
293 if (State[P].num_lines_cleared >= 3) {
294 *blank = (State[P].num_lines_cleared - 2);
295 }
296 }
297 draw_score(screen,P);
298 State[P].num_lines_cleared = 0;
299 return 0;
300 }
301 } else if (count >= 3 && count <= 22) {
302 if (draw)
303 draw_falling(screen, cs->w , &g[0], count - 2);
304
305 /*
306 *delay = max(fall_event_interval / 5,4);
307 */
308 *delay = 4;
309 return count + 1;
310 } else if (count == 23) {
311 fall_down(g);
312 draw_grid(screen,cs,g,draw);
313 if (run_gravity(g))
314 play_sound(ss,SOUND_THUD,0);
315 /*
316 *delay = max(fall_event_interval / 5,4);
317 */
318 *delay = 4;
319 if (determine_falling(&g[0]))
320 return 3;
321 if (check_tetris(g))
322 return 1;
323 else /* cannot be 0: we must redraw without falling */
324 return 2;
325 }
326 return 0; /* not done yet */
327 }
328
329 /***************************************************************************
330 * do_blank()
331 * Blank the visual screen of the given (local) player.
332 ***************************************************************************/
333 static void
do_blank(SDL_Surface * screen,sound_style * ss[2],Grid g[],int P)334 do_blank(SDL_Surface *screen, sound_style *ss[2], Grid g[], int P)
335 {
336 play_sound(ss[P],SOUND_GARBAGE1,1);
337 if (State[P].draw) {
338 State[P].next_draw = SDL_GetTicks() + 1000;
339 State[P].draw_timeout = 1000;
340 SDL_FillRect(screen, &g[P].board,
341 SDL_MapRGB(screen->format,32,32,32));
342 SDL_UpdateSafe(screen, 1, &g[P].board);
343 } else {
344 State[P].next_draw += 1000;
345 State[P].draw_timeout += 1000;
346 }
347 { int i,j;
348 for (j=0;j<g[0].h;j++)
349 for (i=0;i<g[0].w;i++)
350 GRID_CHANGED(distract_grid[P],i,j) = 0;
351 }
352 State[P].draw = 0;
353 }
354
355 /***************************************************************************
356 * bomb_fun()
357 * Function for the bomb special piece.
358 ***************************************************************************/
359 static void
bomb_fun(int x,int y,Grid * g)360 bomb_fun(int x, int y, Grid *g)
361 {
362 if ((x<0 || y<0 || x>=g->w || y>=g->h))
363 return;
364 GRID_SET(*g,x,y,REMOVE_ME);
365 }
366
367 static int most_common = 1;
368
369 /***************************************************************************
370 * colorkill_fun()
371 * Function for the repainting special piece.
372 ***************************************************************************/
373 static void
colorkill_recurse(int x,int y,Grid * g,int target_color)374 colorkill_recurse(int x, int y, Grid *g, int target_color)
375 {
376 if ((x<0 || y<0 || x>=g->w || y>=g->h))
377 return;
378 GRID_CHANGED(*g,x,y) = 1;
379 if (GRID_CONTENT(*g,x,y) != target_color) return;
380 GRID_SET(*g,x,y,REMOVE_ME);
381 colorkill_recurse(x - 1, y, g, target_color);
382 colorkill_recurse(x + 1, y, g, target_color);
383 colorkill_recurse(x, y - 1, g, target_color);
384 colorkill_recurse(x, y + 1, g, target_color);
385 }
386
387 static void
colorkill_fun(int x,int y,Grid * g)388 colorkill_fun(int x, int y, Grid *g)
389 {
390 int c;
391 if ((x<0 || y<0 || x>=g->w || y>=g->h))
392 return;
393 GRID_CHANGED(*g,x,y) = 1;
394 c = GRID_CONTENT(*g,x,y);
395 if (c <= 1 || c == REMOVE_ME) return;
396 GRID_SET(*g,x,y,REMOVE_ME);
397 colorkill_recurse(x - 1, y, g, c);
398 colorkill_recurse(x + 1, y, g, c);
399 colorkill_recurse(x, y - 1, g, c);
400 colorkill_recurse(x, y + 1, g, c);
401 }
402
403 /***************************************************************************
404 * repaint_fun()
405 * Function for the repainting special piece.
406 ***************************************************************************/
407 static void
repaint_fun(int x,int y,Grid * g)408 repaint_fun(int x, int y, Grid *g)
409 {
410 int c;
411 if ((x<0 || y<0 || x>=g->w || y>=g->h))
412 return;
413 GRID_CHANGED(*g,x,y) = 1;
414 c = GRID_CONTENT(*g,x,y);
415 if (c <= 1 || c == most_common) return;
416 GRID_SET(*g,x,y,most_common);
417 repaint_fun(x - 1, y, g);
418 repaint_fun(x + 1, y, g);
419 repaint_fun(x, y - 1, g);
420 repaint_fun(x, y + 1, g);
421 }
422
423 /***************************************************************************
424 * push_down()
425 * Teleport all of the pieces just below this special piece as far down as
426 * they can go in their column.
427 ***************************************************************************/
428 static void
push_down(play_piece * pp,int col,int row,int rot,Grid * g,void (* fun)(int,int,Grid *))429 push_down(play_piece *pp, int col, int row, int rot, Grid *g,
430 void (*fun)(int, int, Grid *))
431 {
432 int i,j,c;
433 int place_y, look_y;
434
435 for (j=0;j<pp->base->dim;j++)
436 for (i=0;i<pp->base->dim;i++)
437 if ((c=BITMAP(*pp->base,rot,i,j))) {
438 int t_x = i + col;
439 int t_y = j + row;
440 if ((t_x<0 || t_y<0 || t_x>=g->w || t_y>=g->h)) {
441 continue;
442 }
443 GRID_SET(*g,t_x,t_y,REMOVE_ME);
444 look_y = t_y + 1;
445 if (look_y >= g->h) continue;
446 if (!GRID_CONTENT(*g,t_x,look_y)) continue;
447 /* OK, try to move look_y down as far as possible */
448 for (place_y = g->h-1;
449 place_y > look_y &&
450 GRID_CONTENT(*g,t_x,place_y) != 0;
451 place_y --)
452 ;
453 if (place_y == look_y) continue;
454 /* otherwise, valid swap! */
455 if (place_y < 0 || place_y >= g->h)
456 continue;
457 if (look_y < 0 || look_y >= g->h)
458 continue;
459 GRID_SET(*g,t_x,place_y, GRID_CONTENT(*g,t_x,look_y));
460 GRID_SET(*g,t_x,look_y, REMOVE_ME);
461 }
462 }
463
464 /***************************************************************************
465 * find_on_board()
466 * Finds where on the board a piece would go: used by special piece
467 * handling routines.
468 ***************************************************************************/
469 static void
find_on_board(play_piece * pp,int col,int row,int rot,Grid * g,void (* fun)(int,int,Grid *))470 find_on_board(play_piece *pp, int col, int row, int rot, Grid *g,
471 void (*fun)(int, int, Grid *))
472 {
473 int i,j,c;
474
475 for (j=0;j<pp->base->dim;j++)
476 for (i=0;i<pp->base->dim;i++)
477 if ((c=BITMAP(*pp->base,rot,i,j))) {
478 int t_x = i + col;
479 int t_y = j + row;
480 if ((t_x<0 || t_y<0 || t_x>=g->w || t_y>=g->h)) {
481 continue;
482 }
483 GRID_SET(*g,t_x,t_y,REMOVE_ME);
484 }
485
486 for (j=0;j<pp->base->dim;j++)
487 for (i=0;i<pp->base->dim;i++)
488 if ((c=BITMAP(*pp->base,rot,i,j))) {
489 int t_x = i + col;
490 int t_y = j + row;
491 if ((t_x<0 || t_y<0 || t_x>=g->w || t_y>=g->h)) {
492 continue;
493 }
494 fun(t_x - 1, t_y, g);
495 fun(t_x + 1, t_y, g);
496 fun(t_x, t_y - 1, g);
497 fun(t_x, t_y + 1, g);
498 }
499 return;
500 }
501
502 /***************************************************************************
503 * most_common_color()
504 * Finds the most common (non-zero, non-garbage) color on the board. If
505 * none, returns the garbage color.
506 ***************************************************************************/
507 static void
most_common_color(Grid * g)508 most_common_color(Grid *g)
509 {
510 int count[256];
511 int x,y,c;
512 int max, max_count;
513
514 memset(count,0, sizeof(count));
515 for (x=0;x<g->w;x++)
516 for (y=0;y<g->h;y++) {
517 c = GRID_CONTENT(*g,x,y);
518 if (c > 1)
519 count[c]++;
520 }
521 max = 1;
522 max_count = 0;
523 for (x=2;x<256;x++)
524 if (count[x] > max_count) {
525 max = x;
526 max_count = count[x];
527 }
528 most_common = max;
529 return;
530 }
531
532 /***************************************************************************
533 * handle_special()
534 * Change the state of the grid based on the magical special piece ...
535 * Look in pos[P] for the current position.
536 *********************************************************************PROTO*/
537 void
handle_special(play_piece * pp,int row,int col,int rot,Grid * g,sound_style * ss)538 handle_special(play_piece *pp, int row, int col, int rot, Grid *g,
539 sound_style *ss)
540 {
541 switch (pp->special) {
542 case No_Special: break;
543 case Special_Bomb:
544 find_on_board(pp, col, row, rot, g, bomb_fun);
545 if (ss)
546 play_sound(ss,SOUND_CLEAR1,256);
547 break;
548 case Special_Repaint:
549 most_common_color(g);
550 find_on_board(pp, col, row, rot, g, repaint_fun);
551 if (ss)
552 play_sound(ss,SOUND_GARBAGE1,256);
553 break;
554 case Special_Pushdown:
555 push_down(pp, col, row, rot, g, repaint_fun);
556 if (ss)
557 play_sound(ss,SOUND_THUD,256*2);
558 break;
559 case Special_Colorkill:
560 find_on_board(pp, col, row, rot, g, colorkill_fun);
561 if (ss)
562 play_sound(ss,SOUND_CLEAR1,256);
563 break;
564
565 }
566 }
567
568 /***************************************************************************
569 * do_pause()
570 * Change the pause status of the local player.
571 ***************************************************************************/
572 static void
do_pause(int paused,Uint32 tv_now,int * pause_begin_time,int * tv_start)573 do_pause(int paused, Uint32 tv_now, int *pause_begin_time, int *tv_start)
574 {
575 int i;
576 draw_pause(paused);
577 if (!paused) {
578 /* fixup times */
579 *tv_start += (tv_now - *pause_begin_time);
580 for (i=0; i<2; i++) {
581 State[i].collide_time += (tv_now - *pause_begin_time);
582 State[i].next_draw += (tv_now - *pause_begin_time);
583 State[i].tv_next_fall += (tv_now - *pause_begin_time);
584 State[i].tv_next_tetris += (tv_now - *pause_begin_time);
585 State[i].tv_next_ai_think += (tv_now - *pause_begin_time);
586 State[i].tv_next_ai_move += (tv_now - *pause_begin_time);
587 }
588 } else {
589 *pause_begin_time = tv_now;
590 }
591 }
592
593 /***************************************************************************
594 * place_this_piece()
595 * Given that the player's current piece structure is already chosen, try
596 * to place it near the top of the board. this may involve shifting it up
597 * a bit or rotating it or something.
598 *
599 * Returns 0 on success.
600 ***************************************************************************/
601 static int
place_this_piece(int P,int blockWidth,Grid g[])602 place_this_piece(int P, int blockWidth, Grid g[])
603 {
604 int Y, R;
605 /* we'll try Y adjustments from -2 to 0 and rotations from 0 to 3 */
606
607 pos[P].x = pos[P].old_x = g[P].board.x + g[P].board.w / 2;
608 for (Y = 0; Y >= -2 ; Y --) {
609 for (R = 0; R <= 3; R++) {
610 pos[P].y = pos[P].old_y = g[P].board.y + (blockWidth * Y);
611 pos[P].old_rot = pos[P].rot = R;
612 if (valid_screen_position(&State[P].cp, blockWidth,
613 &g[P], pos[P].rot, pos[P].x, pos[P].y)) {
614 return 0;
615 }
616 }
617 }
618 return 1; /* no valid position! */
619 }
620
621 /***************************************************************************
622 * event_loop()
623 * The main event-processing dispatch loop.
624 *
625 * Returns 0 on a successful game completion, -1 on a [single-user] quit.
626 *********************************************************************PROTO*/
627 #define NO_PLAYER 0
628 #define HUMAN_PLAYER 1
629 #define AI_PLAYER 2
630 #define NETWORK_PLAYER 3
631 int
event_loop(SDL_Surface * screen,piece_style * ps,color_style * cs[2],sound_style * ss[2],Grid g[],int level[2],int sock,int * seconds_remaining,int time_is_hard_limit,int adjust[],int (* handle)(const SDL_Event *),int seed,int p1,int p2,AI_Player * AI[2])632 event_loop(SDL_Surface *screen, piece_style *ps, color_style *cs[2],
633 sound_style *ss[2], Grid g[], int level[2], int sock,
634 int *seconds_remaining, int time_is_hard_limit,
635 int adjust[], int (*handle)(const SDL_Event *),
636 int seed, int p1, int p2, AI_Player *AI[2])
637 {
638 SDL_Event event;
639 Uint32 tv_now, tv_start;
640 int NUM_PLAYER = 0;
641 int NUM_KEYBOARD = 0;
642 int last_seconds = -1;
643 int minimum_fall_event_interval = 100;
644 int paused = 0;
645 Uint32 pause_begin_time = 0;
646
647 /* measured in milliseconds, 25 frames per second:
648 * 1 frame = 40 milliseconds
649 */
650 int blockWidth = cs[0]->w;
651 int i,j,P, Q;
652
653 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY/Options.key_repeat_delay,
654 SDL_DEFAULT_REPEAT_INTERVAL/2);
655
656 if (gametype != DEMO)
657 stop_all_playing();
658
659 memset(pos, 0, sizeof(pos[0]) * 2);
660 memset(State, 0, sizeof(State[0]) * 2);
661
662 switch (p1) {
663 case NO_PLAYER: Assert(!handle); break;
664 case HUMAN_PLAYER: Assert(!handle); NUM_PLAYER++; NUM_KEYBOARD++; break;
665 case AI_PLAYER: State[0].ai = 1; NUM_PLAYER++; break;
666 case NETWORK_PLAYER: PANIC("Cannot have player 1 over the network!");
667 }
668 switch (p2) {
669 case NO_PLAYER: break;
670 case HUMAN_PLAYER: Assert(!handle); NUM_PLAYER++; NUM_KEYBOARD++; break;
671 case AI_PLAYER: State[1].ai = 1; NUM_PLAYER++; break;
672 case NETWORK_PLAYER: Assert(sock); break;
673 }
674 Assert(NUM_PLAYER >= 1 && NUM_PLAYER <= 2);
675
676 tv_start = tv_now = SDL_GetTicks();
677 tv_start += *seconds_remaining * 1000;
678
679 for (P=0; P<NUM_PLAYER; P++) {
680 State[P].falling = 1;
681 State[P].fall_speed = 1;
682 State[P].tetris_handling = 0;
683 State[P].accept_input = 1;
684 State[P].limbo = 0;
685 State[P].draw = 1;
686 State[P].other_in_limbo = 0;
687 State[P].next_draw = 0;
688 State[P].limbo_sent = 0;
689 State[P].cp = generate_piece(ps, cs[P], seed);
690 State[P].np = generate_piece(ps, cs[P], seed+1);
691 State[P].seed = seed+2;
692 State[P].ready_for_fast = 1;
693 State[P].ready_for_rotate = 1;
694
695 draw_next_piece(screen, ps, cs[P], &State[P].cp, &State[P].np, P);
696
697 adjust[P] = -1;
698
699 if (SPEED_LEVEL(level[P]) <= 7)
700 State[P].fall_event_interval = 45 - SPEED_LEVEL(level[P]) * 5;
701 else
702 State[P].fall_event_interval = 16 - SPEED_LEVEL(level[P]);
703 if (State[P].fall_event_interval < 1)
704 State[P].fall_event_interval = 1;
705
706 if (State[P].fall_event_interval < minimum_fall_event_interval)
707 minimum_fall_event_interval = State[P].fall_event_interval;
708
709 State[P].tv_next_fall = tv_now + State[P].fall_event_interval;
710
711 if (place_this_piece(P, blockWidth, g)) {
712 /* failed to place piece initially ... */
713 pos[P].x = pos[P].old_x = g[P].board.x + g[P].board.w / 2;
714 pos[P].y = pos[P].old_y = g[P].board.y ;
715 pos[P].rot = pos[P].old_rot = 0;
716 }
717
718 if (State[P].ai) {
719 State[P].tv_next_ai_think = tv_now;
720 State[P].tv_next_ai_move = tv_now;
721 if (gametype == DEMO || gametype == AI_VS_AI ||
722 AI[P]->delay_factor == 0) {
723 State[P].ai_interval = State[P].fall_event_interval;
724 if (State[P].ai_interval > 15)
725 State[P].ai_interval = 15;
726 } else {
727 if (AI[P]->delay_factor < 1)
728 AI[P]->delay_factor = 1;
729 if (AI[P]->delay_factor > 100)
730 AI[P]->delay_factor = 100;
731 State[P].ai_interval = AI[P]->delay_factor;
732 }
733 State[P].ai_state = AI[P]->reset(State[P].ai_state, &g[P]);
734 }
735 }
736
737
738 /* generate the fake-out grid: shown when the opponent does something
739 * good! */
740 distract_grid[0] = generate_board(g[0].w,g[0].h,g[0].h-2);
741 distract_grid[0].board = g[0].board;
742
743 SeedRandom(seed);
744 for (i=0;i<g[0].w;i++)
745 for (j=0;j<g[0].h;j++) {
746 GRID_SET(distract_grid[0],i,j,ZEROTO(cs[0]->num_color));
747 }
748 if (NUM_PLAYER == 2) {
749 distract_grid[1] = generate_board(g[1].w,g[1].h,g[1].h-2);
750 distract_grid[1].board = g[1].board;
751 for (i=0;i<g[1].w;i++)
752 for (j=0;j<g[1].h;j++) {
753 GRID_SET(distract_grid[1],i,j,ZEROTO(cs[1]->num_color));
754 }
755 }
756
757 if (sock) {
758 char msg = 'c'; /* WRW: send update */
759 send(sock,&msg,1,0);
760 send(sock,g[0].contents,sizeof(*g[0].contents)
761 * g[0].h * g[0].w,0);
762
763 }
764
765 draw_clock(0);
766
767 draw_grid(screen,cs[0],&g[0],1);
768 draw_score(screen, 0);
769 if (NUM_PLAYER == 2) {
770 draw_grid(screen,cs[1],&g[1],1);
771 draw_score(screen,1);
772 }
773 if (sock)
774 draw_score(screen, 1);
775
776 /*
777 * Major State-Machine Event Loop
778 */
779
780 P = 0;
781
782 while (1) {
783
784 if (NUM_PLAYER == 2)
785 P = !P;
786
787 tv_now = SDL_GetTicks();
788
789 /* update the on-screen clock */
790 if (tv_start >= tv_now)
791 * seconds_remaining = (tv_start - tv_now) / 1000;
792 else
793 * seconds_remaining = - ((tv_now - tv_start) / 1000);
794
795 if (*seconds_remaining != last_seconds && !paused) {
796 last_seconds = *seconds_remaining;
797 draw_clock(*seconds_remaining);
798 if (last_seconds <= 30 && last_seconds >= 0) {
799 play_sound_unless_already_playing(ss[0],SOUND_CLOCK,0);
800 if (NUM_PLAYER == 2)
801 play_sound_unless_already_playing(ss[1],SOUND_CLOCK,0);
802 }
803 }
804
805 /* check for time-out */
806 if (*seconds_remaining < 0 && time_is_hard_limit && !paused) {
807 play_sound(ss[0],SOUND_LEVELDOWN,0);
808 adjust[0] = ADJUST_DOWN;
809 if (NUM_PLAYER == 2) {
810 play_sound(ss[1],SOUND_LEVELDOWN,0);
811 adjust[1] = ADJUST_DOWN;
812 }
813 stop_playing_sound(ss[0],SOUND_CLOCK);
814 if (NUM_PLAYER == 2) stop_playing_sound(ss[1],SOUND_CLOCK);
815 return 0;
816 }
817
818 /*
819 * Visual Events
820 */
821 if (!State[P].draw && tv_now > State[P].next_draw && !paused) {
822 int i,j;
823 State[P].draw = 1;
824 for (i=0;i<g[P].w;i++)
825 for (j=0;j<g[P].h;j++) {
826 GRID_CHANGED(g[P],i,j) = 1;
827 if (GRID_CONTENT(g[P],i,j) == 0)
828 GRID_SET(g[P],i,j,REMOVE_ME);
829 }
830 draw_grid(screen,cs[P],&g[P],1);
831 } else if (!State[P].draw) {
832 int delta = State[P].next_draw - tv_now;
833 int amt = g[P].h - ((g[P].h * delta) / State[P].draw_timeout);
834 int i,j;
835 j = amt - 1;
836 if (j < 0) j = 0;
837 for (i=0;i<g[P].w;i++)
838 GRID_CHANGED(distract_grid[P],i,j) = 1;
839 draw_grid(screen,cs[P],&distract_grid[P],1);
840 }
841
842 /*
843 * Falling Events
844 */
845
846 if (State[P].falling && tv_now >= State[P].tv_next_fall && !paused) {
847 int try;
848 int we_fell = 0;
849
850 #if DEBUG
851 if (tv_now > State[P].tv_next_fall)
852 Debug("Fall: %d %d\n", tv_now, tv_now - State[P].tv_next_fall);
853 #endif
854
855 /* ok, we had a falling event */
856 do {
857 State[P].tv_next_fall += State[P].fall_event_interval;
858 } while (State[P].tv_next_fall <= tv_now);
859
860 for (try = State[P].fall_speed; try > 0; try--)
861 if (valid_screen_position(&State[P].cp,blockWidth,&g[P],pos[P].rot,pos[P].x,pos[P].y+try)) {
862 pos[P].y += try;
863 State[P].fall_speed = try;
864 try = 0;
865 we_fell = 1;
866 }
867 if (!we_fell) {
868 if (!State[P].collide_time) {
869 State[P].collide_time = tv_now +
870 (Options.long_settle_delay ? 400 : 200);
871 continue; /* don't fall */
872 }
873 if (tv_now < State[P].collide_time)
874 continue; /* don't fall */
875 /* do fall! */
876 } else continue;
877 /* collided! */
878
879 State[P].collide_time = 0;
880
881 play_sound(ss[P],SOUND_THUD,0);
882
883 /* this would only come into play if you were halfway
884 * between levels and suddenly someone added garbage */
885 while (!valid_screen_position(&State[P].cp,blockWidth,&g[P],pos[P].rot,pos[P].x,pos[P].y) && pos[P].y > 0)
886 pos[P].y--;
887
888 if (State[P].draw)
889 draw_play_piece(screen, cs[P], &State[P].cp, pos[P].old_x, pos[P].old_y, pos[P].old_rot,
890 &State[P].cp, pos[P].x, pos[P].y, pos[P].rot);
891
892 if (State[P].cp.special != No_Special) {
893 /* handle special powers! */
894 int row, col;
895 screen_to_exact_grid_coords(&g[P], blockWidth,
896 pos[P].x, pos[P].y, &row, &col);
897 handle_special(&State[P].cp, row, col, pos[P].rot, &g[P], ss[P]);
898 } else {
899 /* paste the piece on the board */
900 int row, col;
901 screen_to_exact_grid_coords(&g[P], blockWidth,
902 pos[P].x, pos[P].y, &row, &col);
903 paste_on_board(&State[P].cp, col, row, pos[P].rot, &g[P]);
904 }
905
906 if (sock) {
907 char msg = 'c'; /* WRW: send update */
908 send(sock,&msg,1,0);
909 send(sock,g[P].contents,sizeof(*g[P].contents)
910 * g[P].h * g[P].w,0);
911 }
912 draw_grid(screen,cs[P],&g[P],State[P].draw);
913
914 /* state change */
915 State[P].falling = 0;
916 State[P].fall_speed = 0;
917 State[P].tetris_handling = 1;
918 State[P].accept_input = 0;
919 State[P].tv_next_tetris = tv_now;
920 }
921 /*
922 * Tetris Clear Events
923 */
924 if (State[P].tetris_handling != 0 && tv_now >= State[P].tv_next_tetris && !paused) {
925 int blank = 0, garbage = 0;
926
927 #if DEBUG
928 if (tv_now >= State[P].tv_next_tetris)
929 Debug("Tetr: %d %d (%d)\n", tv_now, tv_now -
930 State[P].tv_next_tetris, State[P].tetris_handling);
931 #endif
932
933 State[P].tetris_handling = tetris_event(
934 &State[P].tetris_event_interval,
935 State[P].tetris_handling, screen, ps, cs[P], ss[P], &g[P],
936 level[P], minimum_fall_event_interval, sock, State[P].draw,
937 &blank, &garbage, P);
938
939 if (NUM_PLAYER == 2) {
940 if (blank)
941 do_blank(screen, ss, g, !P);
942 if (garbage) {
943 add_garbage(&g[!P]);
944 play_sound(ss[!P],SOUND_GARBAGE1,1);
945 draw_grid(screen,cs[!P],&g[!P],State[!P].draw);
946 }
947 }
948
949 tv_now = SDL_GetTicks();
950 do { /* just in case we're *way* behind */
951 State[P].tv_next_tetris += State[P].tetris_event_interval;
952 } while (State[P].tv_next_tetris < tv_now);
953
954 if (State[P].tetris_handling == 0) { /* state change */
955 /* Time for your next piece ...
956 * Yujia points out that we should try a little harder to
957 * fit your piece on the board.
958 */
959 State[P].cp = State[P].np;
960 State[P].np = generate_piece(ps, cs[P], State[P].seed++);
961 draw_next_piece(screen, ps, cs[P],
962 &State[P].cp, &State[P].np, P);
963
964 if (place_this_piece(P, blockWidth, g)) {
965 /* failed to place piece */
966 you_lose:
967 play_sound(ss[P],SOUND_LEVELDOWN,0);
968 adjust[P] = ADJUST_DOWN;
969 if (NUM_PLAYER == 2 && !sock)
970 adjust[!P] = ADJUST_SAME;
971 if (sock == 0) {
972 stop_playing_sound(ss[0],SOUND_CLOCK);
973 if (NUM_PLAYER == 2) stop_playing_sound(ss[1],SOUND_CLOCK);
974 return 0;
975 }
976 /*
977 Debug("Entering Limbo: adjust down.\n");
978 */
979 State[P].falling = 0;
980 State[P].fall_speed = 0;
981 State[P].tetris_handling = 0;
982 State[P].accept_input = 0;
983 State[P].limbo = 1;
984 } else {
985 int x,y,count = 0;
986 if (State[P].ai)
987 State[P].ai_state =
988 AI[P]->reset(State[P].ai_state, &g[P]);
989 for (y=0;y<g->h;y++)
990 for (x=0;x<g->w;x++)
991 if (GRID_CONTENT(g[P],x,y) == 1)
992 count++;
993 if (count == 0) {
994 you_win:
995 play_sound(ss[P],SOUND_LEVELUP,256);
996 if (*seconds_remaining <= 0) {
997 adjust[P] = ADJUST_SAME;
998 if (NUM_PLAYER == 2 && !sock)
999 adjust[!P] = ADJUST_DOWN;
1000 } else {
1001 adjust[P] = ADJUST_UP;
1002 if (NUM_PLAYER == 2 && !sock)
1003 adjust[!P] = ADJUST_SAME;
1004 }
1005 if (sock == 0) {
1006 stop_playing_sound(ss[0],SOUND_CLOCK);
1007 if (NUM_PLAYER == 2) stop_playing_sound(ss[1],SOUND_CLOCK);
1008 return 0;
1009 }
1010 /*
1011 Debug("Entering Limbo: you win, adjust ?/?.\n");
1012 */
1013 State[P].falling = 0;
1014 State[P].fall_speed = 0;
1015 State[P].tetris_handling = 0;
1016 State[P].accept_input = 0;
1017 State[P].limbo = 1;
1018 } else {
1019 /* keep playing */
1020 State[P].falling = 1;
1021 State[P].fall_speed = 1;
1022 State[P].tetris_handling = 0;
1023 State[P].accept_input = 1;
1024 State[P].tv_next_fall = SDL_GetTicks() +
1025 State[P].fall_event_interval;
1026 }
1027 }
1028 }
1029 } /* end: handle tetris events */
1030
1031 /*
1032 * AI Events
1033 */
1034 if (State[P].ai && tv_now >= State[P].tv_next_ai_think && !paused) {
1035 int row, col;
1036 #ifdef AI_THINK_TIME
1037 Uint32 tv_before = SDL_GetTicks();
1038 #endif
1039
1040 screen_to_grid_coords(&g[P], blockWidth, pos[P].x, pos[P].y, &row, &col);
1041
1042 /* simulate blanked screens */
1043 if (State[P].draw)
1044 AI[P]->think(State[P].ai_state, &g[P], &State[P].cp, &State[P].np, col, row, pos[P].rot);
1045
1046 #ifdef AI_THINK_TIME
1047 tv_now = SDL_GetTicks();
1048 if (tv_now > tv_before + 1)
1049 Debug("AI[%s] took too long in think() [%d ticks].\n",
1050 AI[P]->name, tv_now - tv_before);
1051 #endif
1052
1053 do { /* just in case we're *way* behind */
1054 State[P].tv_next_ai_think +=
1055 State[P].ai_interval;
1056 } while (State[P].tv_next_ai_think < tv_now);
1057 }
1058 if (State[P].ai && State[P].accept_input &&
1059 tv_now >= State[P].tv_next_ai_move && !paused) {
1060 int row, col;
1061 #ifdef AI_THINK_TIME
1062 Uint32 tv_before = SDL_GetTicks();
1063 #endif
1064
1065 screen_to_grid_coords(&g[P], blockWidth, pos[P].x, pos[P].y, &row, &col);
1066 pos[P].move =
1067 AI[P]->move(State[P].ai_state, &g[P], &State[P].cp, &State[P].np,
1068 col, row, pos[P].rot);
1069 #ifdef AI_THINK_TIME
1070 tv_now = SDL_GetTicks();
1071 if (tv_now > tv_before + 1)
1072 Debug("AI[%s] took too long in move() [%d ticks].\n",
1073 AI[P]->name, tv_now - tv_before);
1074 #endif
1075
1076 do { /* just in case we're *way* behind */
1077 State[P].tv_next_ai_move +=
1078 State[P].ai_interval * 5;
1079 } while (State[P].tv_next_ai_move < tv_now);
1080 }
1081
1082 /*
1083 * User Interface Events
1084 */
1085
1086 if (SDL_PollEvent(&event)) {
1087
1088 /* special menu handling! */
1089 if (handle) {
1090 if (handle(&event)) {
1091 return -1;
1092 }
1093 } else switch (event.type) {
1094 case SDL_KEYUP:
1095 /* "down" will not affect you again until you release
1096 * the down key and press it again */
1097 if (event.key.keysym.sym == SDLK_DOWN) {
1098 State[1].ready_for_fast = 1;
1099 if (NUM_KEYBOARD == 1)
1100 State[0].ready_for_fast = 1;
1101 }
1102 else if (event.key.keysym.sym == SDLK_UP) {
1103 State[1].ready_for_rotate = 1;
1104 if (NUM_KEYBOARD == 1)
1105 State[0].ready_for_rotate = 1;
1106 }
1107 else if (event.key.keysym.sym == SDLK_w)
1108 State[0].ready_for_rotate = 1;
1109 else if (event.key.keysym.sym == SDLK_s)
1110 State[0].ready_for_fast = 1;
1111 else if (event.key.keysym.sym == SDLK_1) {
1112 P = 0;
1113 goto you_win;
1114 }
1115 else if (event.key.keysym.sym == SDLK_2) {
1116 P = 0;
1117 goto you_lose;
1118 }
1119 else if (event.key.keysym.sym == SDLK_3) {
1120 P = 1;
1121 goto you_win;
1122 }
1123 else if (event.key.keysym.sym == SDLK_4) {
1124 P = 1;
1125 goto you_lose;
1126 } else if (event.key.keysym.sym == SDLK_p && gametype != DEMO) {
1127 /* Pause it! */
1128 tv_now = SDL_GetTicks();
1129 paused = !paused;
1130 if (sock) {
1131 char msg = 'p'; /* WRW: send pause update */
1132 send(sock,&msg,1,0);
1133 }
1134 do_pause(paused, tv_now, &pause_begin_time, &tv_start);
1135 }
1136
1137 break;
1138
1139 case SDL_KEYDOWN:
1140 {
1141 int ks = event.key.keysym.sym;
1142 Q = -1;
1143
1144 /* keys for P=0 */
1145 if (ks == SDLK_UP || ks == SDLK_DOWN ||
1146 ks == SDLK_RIGHT || ks == SDLK_LEFT) {
1147 Q = 1;
1148 } else if (ks == SDLK_w || ks == SDLK_s ||
1149 ks == SDLK_a || ks == SDLK_d) {
1150 Q = 0;
1151 } else if (ks == SDLK_q) {
1152 if (sock == 0) {
1153 adjust[0] = -1;
1154 if (NUM_PLAYER == 2)
1155 adjust[1] = -1;
1156 stop_playing_sound(ss[0],SOUND_CLOCK);
1157 if (NUM_PLAYER == 2) stop_playing_sound(ss[1],SOUND_CLOCK);
1158 return -1;
1159 } else {
1160 /*
1161 Debug("Entering Limbo: adjust down.\n");
1162 */
1163 State[0].falling = 0;
1164 State[0].fall_speed = 0;
1165 State[0].tetris_handling = 0;
1166 State[0].accept_input = 0;
1167 State[0].limbo = 1;
1168 adjust[0] = ADJUST_DOWN;
1169 }
1170 } else if ((ks == SDLK_RETURN) &&
1171 ((event.key.keysym.mod & KMOD_LCTRL) ||
1172 (event.key.keysym.mod & KMOD_RCTRL))) {
1173 SDL_WM_ToggleFullScreen(screen);
1174 break;
1175 } else break;
1176 if (NUM_KEYBOARD == 1) Q = 0;
1177 else if (NUM_KEYBOARD < 1) break;
1178 /* humans cannot modify AI moves! */
1179
1180 Assert(Q == 0 || Q == 1);
1181
1182 if (event.key.keysym.sym != SDLK_DOWN &&
1183 event.key.keysym.sym != SDLK_s)
1184 State[Q].fall_speed = 1;
1185 if (!State[Q].accept_input) {
1186 break;
1187 }
1188 /* only if we are accepting input */
1189 switch (event.key.keysym.sym) {
1190 case SDLK_UP: case SDLK_w:
1191 if (State[Q].ready_for_rotate)
1192 pos[Q].move = MOVE_ROTATE;
1193 break;
1194 case SDLK_DOWN: case SDLK_s:
1195 if (State[Q].ready_for_fast)
1196 pos[Q].move = MOVE_DOWN;
1197 break;
1198 case SDLK_LEFT: case SDLK_a:
1199 pos[Q].move = MOVE_LEFT; break;
1200 case SDLK_RIGHT: case SDLK_d:
1201 pos[Q].move = MOVE_RIGHT; break;
1202 default:
1203 PANIC("unknown keypress");
1204 }
1205 }
1206 break;
1207 case SDL_QUIT:
1208 Debug("Window-manager exit request.\n");
1209 adjust[0] = -1;
1210 if (NUM_PLAYER == 2)
1211 adjust[1] = -1;
1212 stop_playing_sound(ss[0],SOUND_CLOCK);
1213 if (NUM_PLAYER == 2) stop_playing_sound(ss[1],SOUND_CLOCK);
1214 return -1;
1215 case SDL_SYSWMEVENT:
1216 break;
1217 } /* end: switch (event.type) */
1218 }
1219
1220 /*
1221 * Handle Movement
1222 */
1223 for (Q=0;Q<NUM_PLAYER;Q++) {
1224 switch (pos[Q].move) {
1225 case MOVE_ROTATE:
1226 State[Q].ready_for_rotate = 0;
1227 if (valid_screen_position(&State[Q].cp,blockWidth,&g[Q],(pos[Q].rot+1)%4,pos[Q].x,pos[Q].y)) {
1228 pos[Q].rot = (pos[Q].rot+1)%4;
1229 State[Q].collide_time = 0;
1230 } else if (valid_screen_position(&State[Q].cp,blockWidth,&g[Q],(pos[Q].rot+1)%4,
1231 pos[Q].x-blockWidth,pos[Q].y)) {
1232 pos[Q].rot = (pos[Q].rot+1)%4;
1233 pos[Q].x -= blockWidth;
1234 State[Q].collide_time = 0;
1235 } else if (valid_screen_position(&State[Q].cp,blockWidth,&g[Q],(pos[Q].rot+1)%4,
1236 pos[Q].x+blockWidth,pos[Q].y)) {
1237 pos[Q].rot = (pos[Q].rot+1)%4;
1238 pos[Q].x += blockWidth;
1239 State[Q].collide_time = 0;
1240 } else if (valid_screen_position(&State[Q].cp,blockWidth,&g[Q],(pos[Q].rot+1)%4,
1241 pos[Q].x,pos[Q].y+blockWidth)) {
1242 pos[Q].rot = (pos[Q].rot+1)%4;
1243 pos[Q].y += blockWidth;
1244 State[Q].collide_time = 0;
1245 } else if (Options.upward_rotation &&
1246 valid_screen_position(&State[Q].cp,blockWidth,
1247 &g[Q],(pos[Q].rot+1)%4, pos[Q].x,
1248 pos[Q].y-blockWidth)) {
1249 pos[Q].rot = (pos[Q].rot+1)%4;
1250 pos[Q].y -= blockWidth;
1251 State[Q].collide_time = 0;
1252 }
1253 pos[Q].move = MOVE_NONE;
1254 break;
1255 case MOVE_LEFT:
1256 for (i=0;i<10;i++)
1257 if (valid_screen_position(&State[Q].cp,blockWidth,
1258 &g[Q],pos[Q].rot,
1259 pos[Q].x-blockWidth,
1260 pos[Q].y+i)) {
1261 pos[Q].x -= blockWidth;
1262 pos[Q].y += i;
1263 State[Q].collide_time = 0;
1264 break;
1265 }
1266 pos[Q].move = MOVE_NONE;
1267 break;
1268 case MOVE_RIGHT:
1269 for (i=0;i<10;i++)
1270 if (valid_screen_position(&State[Q].cp,blockWidth,&g[Q],pos[Q].rot,
1271 pos[Q].x+blockWidth,pos[Q].y+i)){
1272 pos[Q].x += blockWidth;
1273 pos[Q].y += i;
1274 State[Q].collide_time = 0;
1275 break;
1276 }
1277 pos[Q].move = MOVE_NONE;
1278 break;
1279 case MOVE_DOWN:
1280 if (valid_screen_position(&State[Q].cp,blockWidth,&g[Q],pos[Q].rot,
1281 pos[Q].x, pos[Q].y+blockWidth))
1282 pos[Q].y+=blockWidth;
1283 State[Q].fall_speed = 20;
1284 State[Q].ready_for_fast = 0;
1285 pos[Q].move = MOVE_NONE;
1286 break;
1287 default:
1288 break;
1289 }
1290 } /* endof: for each player, check move */
1291
1292
1293 if (State[P].falling && !paused) {
1294 if (State[P].draw)
1295 if (pos[P].old_x != pos[P].x || pos[P].old_y != pos[P].y || pos[P].old_rot != pos[P].rot) {
1296 draw_play_piece(screen, cs[P], &State[P].cp, pos[P].old_x, pos[P].old_y, pos[P].old_rot,
1297 &State[P].cp, pos[P].x, pos[P].y, pos[P].rot);
1298 pos[P].old_x = pos[P].x; pos[P].old_y = pos[P].y; pos[P].old_rot = pos[P].rot;
1299 }
1300 }
1301
1302 /* network connection */
1303 if (sock) {
1304 fd_set read_fds;
1305 struct timeval timeout = { 0, 0 };
1306 int retval;
1307
1308 Assert(P == 0);
1309
1310 do {
1311 FD_ZERO(&read_fds);
1312 FD_SET(sock,&read_fds);
1313 #if HAVE_SELECT || HAVE_WINSOCK_H
1314 retval = select(sock+1, &read_fds, NULL, NULL, &timeout);
1315 #else
1316 #warning "Since you do not have select(), networking play will fail."
1317 retval = 0;
1318 #endif
1319 if (retval > 0) {
1320 char msg;
1321 if (recv(sock,&msg,1,0) != 1) {
1322 Debug("WARNING: Other player has left?\n");
1323 close(sock);
1324 sock = 0;
1325 retval = 0;
1326 } else {
1327 switch (msg) {
1328 case 'b':
1329 do_blank(screen, ss, g, P);
1330 break;
1331 case 'p':
1332 paused = !paused;
1333 tv_now = SDL_GetTicks();
1334 do_pause(paused, tv_now, &pause_begin_time, &tv_start);
1335 break;
1336
1337 case ADJUST_DOWN: /* other play in limbo */
1338 case ADJUST_SAME:
1339 case ADJUST_UP:
1340 State[P].other_in_limbo = 1;
1341 adjust[!P] = msg;
1342 break;
1343
1344 case 'g':
1345 add_garbage(&g[P]);
1346 play_sound(ss[P],SOUND_GARBAGE1,1);
1347 draw_grid(screen,cs[P],&g[P],State[P].draw);
1348 break;
1349 case 's':
1350 recv(sock,(char *)&Score[1], sizeof(Score[1]),0);
1351 draw_score(screen, 1);
1352 break;
1353 case 'c': { int i,j;
1354 memcpy(g[!P].temp,g[!P].contents,
1355 sizeof(*g[0].temp) *
1356 g[!P].w * g[!P].h);
1357 recv(sock,g[!P].contents,
1358 sizeof(*g[!P].contents) *
1359 g[!P].w * g[!P].h,0);
1360 for (i=0;i<g[!P].w;i++)
1361 for (j=0;j<g[!P].h;j++)
1362 if (GRID_CONTENT(g[!P],i,j) != TEMP_CONTENT(g[1],i,j)){
1363 GRID_CHANGED(g[!P],i,j)=1;
1364 if (i > 0) GRID_CHANGED(g[!P],i-1,j) = 1;
1365 if (j > 0) GRID_CHANGED(g[!P],i,j-1) = 1;
1366 if (i < g[!P].w-1) GRID_CHANGED(g[!P],i+1,j) = 1;
1367 if (i < g[!P].h-1) GRID_CHANGED(g[!P],i,j+1) = 1;
1368 if (GRID_CONTENT(g[!P],i,j) == 0) GRID_SET(g[!P],i,j,REMOVE_ME);
1369 }
1370 draw_grid(screen,cs[!P],&g[!P],1);
1371 }
1372 break;
1373 default: break;
1374 }
1375 }
1376 }
1377 } while (retval > 0 &&
1378 !(State[P].limbo && State[P].other_in_limbo));
1379
1380 /* limbo handling */
1381 if (State[P].limbo && State[P].other_in_limbo) {
1382 Assert(adjust[0] != -1 && adjust[1] != -1);
1383 stop_playing_sound(ss[0],SOUND_CLOCK);
1384 if (NUM_PLAYER == 2) stop_playing_sound(ss[1],SOUND_CLOCK);
1385 return 0;
1386 } else if (State[P].limbo && !State[P].other_in_limbo &&
1387 !State[P].limbo_sent) {
1388 char msg = adjust[P]; /* WRW: send update */
1389 State[P].limbo_sent = 1;
1390 send(sock,&msg,1,0);
1391 } else if (!State[P].limbo && State[P].other_in_limbo) {
1392 char msg; /* WRW: send update */
1393 /* hmm, other guy is done ... */
1394 if (adjust[!P] == ADJUST_UP || adjust[!P] == ADJUST_SAME) {
1395 if (*seconds_remaining > 0)
1396 adjust[P] = ADJUST_SAME;
1397 else
1398 adjust[P] = ADJUST_DOWN;
1399 } else if (adjust[!P] == ADJUST_DOWN) {
1400 adjust[P] = ADJUST_SAME;
1401 }
1402 /*
1403 Debug("Entering Limbo: adjust same/down.\n");
1404 */
1405 State[P].falling = 0;
1406 State[P].fall_speed = 0;
1407 State[P].tetris_handling = 0;
1408 State[P].accept_input = 0;
1409 State[P].limbo = 1;
1410 State[P].limbo_sent = 1;
1411 msg = adjust[P];
1412 send(sock,&msg,1,0);
1413 stop_playing_sound(ss[0],SOUND_CLOCK);
1414 if (NUM_PLAYER == 2) stop_playing_sound(ss[1],SOUND_CLOCK);
1415 return 0;
1416 }
1417 }
1418 if (paused) {
1419 atris_run_flame();
1420 }
1421
1422 tv_now = SDL_GetTicks();
1423 {
1424 Uint32 least = State[0].tv_next_fall;
1425
1426 if (State[0].tetris_handling && State[0].tv_next_tetris < least)
1427 least = State[0].tv_next_tetris;
1428 if (State[0].ai && State[0].tv_next_ai_think < least)
1429 least = State[0].tv_next_ai_think;
1430 if (State[0].ai && State[0].tv_next_ai_move < least)
1431 least = State[0].tv_next_ai_move;
1432 if (NUM_PLAYER == 2) {
1433 if (State[1].tv_next_fall < least)
1434 least = State[1].tv_next_tetris;
1435 if (State[1].tetris_handling && State[1].tv_next_tetris < least)
1436 least = State[1].tv_next_tetris;
1437 if (State[1].ai && State[1].tv_next_ai_think < least)
1438 least = State[1].tv_next_ai_think;
1439 if (State[1].ai && State[1].tv_next_ai_move < least)
1440 least = State[1].tv_next_ai_move;
1441 }
1442
1443 if (least >= tv_now + 4 && !SDL_PollEvent(NULL)) {
1444 /* hey, we could sleep for two ... */
1445 if (State[0].ai || State[1].ai) {
1446 SDL_Delay(1);
1447 if (State[0].ai && State[0].draw) {
1448 int row, col;
1449 screen_to_grid_coords(&g[0], blockWidth, pos[0].x, pos[0].y, &row, &col);
1450 AI[0]->think(State[0].ai_state, &g[0], &State[0].cp, &State[0].np, col, row, pos[0].rot);
1451 } else SDL_Delay(1);
1452 if (State[1].ai && State[1].draw) {
1453 int row, col;
1454 screen_to_grid_coords(&g[1], blockWidth, pos[1].x, pos[1].y, &row, &col);
1455 AI[1]->think(State[1].ai_state, &g[1], &State[1].cp, &State[1].np, col, row, pos[1].rot);
1456 } else SDL_Delay(1);
1457 } else SDL_Delay(2);
1458 } else if (least > tv_now && !SDL_PollEvent(NULL))
1459 SDL_Delay(least - tv_now);
1460 }
1461 }
1462 }
1463
1464 /*
1465 * $Log: event.c,v $
1466 * Revision 1.61 2001/01/05 21:12:32 weimer
1467 * advance to atris 1.0.5, add support for ".atrisrc" and changing the
1468 * keyboard repeat rate
1469 *
1470 * Revision 1.60 2000/11/10 18:16:48 weimer
1471 * changes for Atris 1.0.4 - three new special options
1472 *
1473 * Revision 1.59 2000/11/06 04:39:56 weimer
1474 * networking consistency check for power pieces
1475 *
1476 * Revision 1.58 2000/11/06 04:06:44 weimer
1477 * option menu
1478 *
1479 * Revision 1.57 2000/11/06 01:25:54 weimer
1480 * add in the other special piece
1481 *
1482 * Revision 1.56 2000/11/06 00:51:55 weimer
1483 * fixed the paint thing so that it actually paints
1484 *
1485 * Revision 1.55 2000/11/06 00:24:01 weimer
1486 * add WalkRadioGroup modifications (inactive field for Kiri) and some support
1487 * for special pieces
1488 *
1489 * Revision 1.54 2000/11/03 04:25:58 weimer
1490 * add some optimizations to run_gravity to make it just a bit faster (down
1491 * to 0.01 ms/call from 0.02), sleep a bit more in event-loop: generally
1492 * trying to make us more CPU friendly ...
1493 *
1494 * Revision 1.53 2000/11/02 03:06:20 weimer
1495 * better interface for walk-radio menus: we are now ready for Kiri to change
1496 * them to add run-time options ...
1497 *
1498 * Revision 1.52 2000/11/01 17:55:33 weimer
1499 * remove ambiguous references to "you win" in debugging output
1500 *
1501 * Revision 1.51 2000/11/01 04:39:41 weimer
1502 * clear the little scoring spot correctly, updates for making a no-sound
1503 * distribution
1504 *
1505 * Revision 1.50 2000/11/01 03:53:06 weimer
1506 * modifications for version 1.0.1: you can pick your starting level, you can
1507 * pick the AI difficulty factor, the game is better about placing new pieces
1508 * when there is garbage, when things fall out of your control they now fall
1509 * at a uniform rate ...
1510 *
1511 * Revision 1.49 2000/10/30 16:57:24 weimer
1512 * minor doc fixups
1513 *
1514 * Revision 1.48 2000/10/30 16:25:25 weimer
1515 * display the network player score during network games. Also give the
1516 * non-server a little message when waiting for the server to go on.
1517 *
1518 * Revision 1.47 2000/10/29 22:55:01 weimer
1519 * networking consistency checks (you must have the same number of doodads):
1520 * special hotkey 'f' in main menu toggles full screen mode
1521 * added depth specification on the command line
1522 * automatically search for the darkest non-black color ...
1523 *
1524 * Revision 1.46 2000/10/29 21:23:28 weimer
1525 * One last round of header-file changes to reflect my newest and greatest
1526 * knowledge of autoconf/automake. Now if you fail to have some bizarro
1527 * function, we try to go on anyway unless it is vastly needed.
1528 *
1529 * Revision 1.45 2000/10/29 19:04:32 weimer
1530 * minor highscore handling changes: new filename, use the draw_string() and
1531 * draw_bordered_rect() and input_string() interfaces, handle the widget layer
1532 * and the flame layer, etc. Also fix a minor bug where you would be prevented
1533 * from settling if you pressed a key even if it didn't really move you. :-)
1534 *
1535 * Revision 1.44 2000/10/29 17:23:13 weimer
1536 * incorporate "xflame" flaming background for added spiffiness ...
1537 *
1538 * Revision 1.43 2000/10/29 00:17:39 weimer
1539 * added support for a system independent random number generator
1540 *
1541 * Revision 1.42 2000/10/28 13:39:14 weimer
1542 * added a pausing feature ...
1543 *
1544 * Revision 1.41 2000/10/27 00:07:36 weimer
1545 * some sound fixes ...
1546 *
1547 * Revision 1.40 2000/10/26 22:23:07 weimer
1548 * double the effective number of levels
1549 *
1550 * Revision 1.39 2000/10/22 22:05:51 weimer
1551 * Added a few new sounds ...
1552 *
1553 * Revision 1.38 2000/10/21 01:14:42 weimer
1554 * massic autoconf/automake restructure ...
1555 *
1556 * Revision 1.37 2000/10/20 01:32:09 weimer
1557 * Minor play issue problems -- time is now truly a hard limit!
1558 *
1559 * Revision 1.36 2000/10/19 22:30:55 weimer
1560 * make pieces fall a bit faster
1561 *
1562 * Revision 1.35 2000/10/19 22:06:51 weimer
1563 * minor changes ...
1564 *
1565 * Revision 1.34 2000/10/19 00:20:27 weimer
1566 * sound directory changes, added a ticking clock ...
1567 *
1568 * Revision 1.33 2000/10/18 23:57:49 weimer
1569 * general fixup, color changes, display changes.
1570 * Notable: "Safe" Blits and Updates now perform "clipping". No more X errors,
1571 * we hope!
1572 *
1573 * Revision 1.32 2000/10/18 03:29:56 weimer
1574 * fixed problem with "machine-gun" sounds caused by "run_gravity()" returning
1575 * 1 too often
1576 *
1577 * Revision 1.31 2000/10/18 02:04:02 weimer
1578 * playability changes ...
1579 *
1580 * Revision 1.30 2000/10/14 16:17:41 weimer
1581 * level adjustment changes, added some new AIs, etc.
1582 *
1583 * Revision 1.29 2000/10/14 01:42:53 weimer
1584 * better scoring of thumbs-up, thumbs-down
1585 *
1586 * Revision 1.28 2000/10/14 01:24:34 weimer
1587 * fixed error with not advancing levels when fighting AI
1588 *
1589 * Revision 1.27 2000/10/14 00:58:32 weimer
1590 * fixed an unsigned arithmetic problem that was sleeping too often!
1591 *
1592 * Revision 1.26 2000/10/14 00:56:41 weimer
1593 * whoops, minor boolean typo!
1594 *
1595 * Revision 1.25 2000/10/14 00:42:27 weimer
1596 * fixed minor error where you couldn't move all the way to the left ...
1597 *
1598 * Revision 1.24 2000/10/13 22:34:26 weimer
1599 * minor wessy AI changes
1600 *
1601 * Revision 1.23 2000/10/13 18:23:28 weimer
1602 * fixed a race condition in tetris_event()
1603 *
1604 * Revision 1.22 2000/10/13 17:55:36 weimer
1605 * Added another color "Style" ...
1606 *
1607 * Revision 1.21 2000/10/13 16:37:39 weimer
1608 * Changed the AI so that it now passes state around via void pointers, rather
1609 * than using global variables. This allows the same AI to play itself. Also
1610 * changed the "AI vs. AI" display so that you can keep track of total wins
1611 * and losses.
1612 *
1613 * Revision 1.20 2000/10/13 15:41:53 weimer
1614 * revamped AI support, now you can pick your AI and have AI duels (such fun!)
1615 * the mighty Aliz AI still crashes a bit, though ... :-)
1616 *
1617 * Revision 1.19 2000/10/13 03:02:05 wkiri
1618 * Added Aliz (Kiri AI) to ai.c.
1619 * Note event.c currently calls Aliz, not Wessy AI.
1620 *
1621 * Revision 1.18 2000/10/12 22:21:25 weimer
1622 * display changes, more multi-local-play threading (e.g., myScore ->
1623 * Score[0]), that sort of thing ...
1624 *
1625 * Revision 1.17 2000/10/12 19:17:08 weimer
1626 * Further support for AI players and multiple game types.
1627 *
1628 * Revision 1.16 2000/10/12 00:49:08 weimer
1629 * added "AI" support and walking radio menus for initial game configuration
1630 *
1631 * Revision 1.15 2000/09/09 17:05:35 wkiri
1632 * Hideous log changes (Wes: how dare you include a comment character!)
1633 *
1634 * Revision 1.14 2000/09/09 16:58:27 weimer
1635 * Sweeping Change of Ultimate Mastery. Main loop restructuring to clean up
1636 * main(), isolate the behavior of the three game types. Move graphic files
1637 * into graphics/-, style files into styles/-, remove some unused files,
1638 * add game flow support (breaks between games, remembering your level within
1639 * this game), multiplayer support for the event loop, some global variable
1640 * cleanup. All that and a bag of chips!
1641 *
1642 * Revision 1.13 2000/09/05 22:10:46 weimer
1643 * fixed initial garbage generation strangeness
1644 *
1645 * Revision 1.12 2000/09/05 21:47:41 weimer
1646 * major timing revamping: it actually seems to work now!
1647 *
1648 * Revision 1.11 2000/09/05 20:22:12 weimer
1649 * native video mode selection, timing investigation
1650 *
1651 * Revision 1.10 2000/09/04 15:20:21 weimer
1652 * faster blitting algorithms for drawing the grid and drawing falling pieces
1653 * (when you eliminate a line)
1654 *
1655 * Revision 1.9 2000/09/04 14:18:09 weimer
1656 * flushed out the pattern color so that it has the same number of colors as
1657 * the default
1658 *
1659 * Revision 1.8 2000/09/03 20:58:22 weimer
1660 * yada
1661 *
1662 * Revision 1.7 2000/09/03 20:57:02 weimer
1663 * changes the time variable to be passed by reference in the event loop
1664 *
1665 * Revision 1.6 2000/09/03 20:34:42 weimer
1666 * only draw their score in multiplayer game
1667 *
1668 * Revision 1.5 2000/09/03 20:27:38 weimer
1669 * the event loop actually works!
1670 *
1671 * Revision 1.4 2000/09/03 19:59:56 weimer
1672 * wessy event-loop changes
1673 *
1674 * Revision 1.3 2000/09/03 18:44:36 wkiri
1675 * Cleaned up atris.c (see high_score_check()).
1676 * Added game type (defaults to MARATHON).
1677 *
1678 */
1679