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