rnd-20060308-1-src
[rocksndiamonds.git] / src / game_em / graphics.c
1 /* 2000-08-13T14:36:17Z
2  *
3  * graphics manipulation crap
4  */
5
6 #include "global.h"
7 #include "display.h"
8 #include "level.h"
9
10 #define MIN_SCREEN_XPOS         1
11 #define MIN_SCREEN_YPOS         1
12 #define MAX_SCREEN_XPOS         MAX(1, lev.width  - (SCR_FIELDX - 1))
13 #define MAX_SCREEN_YPOS         MAX(1, lev.height - (SCR_FIELDY - 1))
14
15 #define MIN_SCREEN_X            (MIN_SCREEN_XPOS * TILEX)
16 #define MIN_SCREEN_Y            (MIN_SCREEN_YPOS * TILEY)
17 #define MAX_SCREEN_X            (MAX_SCREEN_XPOS * TILEX)
18 #define MAX_SCREEN_Y            (MAX_SCREEN_YPOS * TILEY)
19
20 #define VALID_SCREEN_X(x)       ((x) < MIN_SCREEN_X ? MIN_SCREEN_X :    \
21                                  (x) > MAX_SCREEN_X ? MAX_SCREEN_X : (x))
22 #define VALID_SCREEN_Y(y)       ((y) < MIN_SCREEN_Y ? MIN_SCREEN_Y :    \
23                                  (y) > MAX_SCREEN_Y ? MAX_SCREEN_Y : (y))
24
25 #define PLAYER_SCREEN_X(p)      (((    frame) * ply[p].oldx +           \
26                                   (8 - frame) * ply[p].x) * TILEX / 8   \
27                                  - ((SCR_FIELDX - 1) * TILEX) / 2)
28 #define PLAYER_SCREEN_Y(p)      (((    frame) * ply[p].oldy +           \
29                                   (8 - frame) * ply[p].y) * TILEY / 8   \
30                                  - ((SCR_FIELDY - 1) * TILEY) / 2)
31
32
33 int frame;                      /* current screen frame */
34 int screen_x;                   /* current scroll position */
35 int screen_y;
36
37 /* tiles currently on screen */
38 static int screentiles[MAX_BUF_YSIZE][MAX_BUF_XSIZE];
39 static int crumbled_state[MAX_BUF_YSIZE][MAX_BUF_XSIZE];
40
41 static boolean redraw[MAX_BUF_XSIZE][MAX_BUF_YSIZE];
42
43 #if 1
44 int centered_player_nr;
45 #else
46 static int centered_player_nr;
47 #endif
48
49 /* copy the entire screen to the window at the scroll position */
50
51 void BlitScreenToBitmap_EM(Bitmap *target_bitmap)
52 {
53   int x = screen_x % (MAX_BUF_XSIZE * TILEX);
54   int y = screen_y % (MAX_BUF_YSIZE * TILEY);
55
56   if (x < 2 * TILEX && y < 2 * TILEY)
57   {
58     BlitBitmap(screenBitmap, target_bitmap, x, y,
59                SCR_FIELDX * TILEX, SCR_FIELDY * TILEY, SX, SY);
60   }
61   else if (x < 2 * TILEX && y >= 2 * TILEY)
62   {
63     BlitBitmap(screenBitmap, target_bitmap, x, y,
64                SCR_FIELDX * TILEX, MAX_BUF_YSIZE * TILEY - y,
65                SX, SY);
66     BlitBitmap(screenBitmap, target_bitmap, x, 0,
67                SCR_FIELDX * TILEX, y - 2 * TILEY,
68                SX, SY + MAX_BUF_YSIZE * TILEY - y);
69   }
70   else if (x >= 2 * TILEX && y < 2 * TILEY)
71   {
72     BlitBitmap(screenBitmap, target_bitmap, x, y,
73                MAX_BUF_XSIZE * TILEX - x, SCR_FIELDY * TILEY,
74                SX, SY);
75     BlitBitmap(screenBitmap, target_bitmap, 0, y,
76                x - 2 * TILEX, SCR_FIELDY * TILEY,
77                SX + MAX_BUF_XSIZE * TILEX - x, SY);
78   }
79   else
80   {
81     BlitBitmap(screenBitmap, target_bitmap, x, y,
82                MAX_BUF_XSIZE * TILEX - x, MAX_BUF_YSIZE * TILEY - y,
83                SX, SY);
84     BlitBitmap(screenBitmap, target_bitmap, 0, y,
85                x - 2 * TILEX, MAX_BUF_YSIZE * TILEY - y,
86                SX + MAX_BUF_XSIZE * TILEX - x, SY);
87     BlitBitmap(screenBitmap, target_bitmap, x, 0,
88                MAX_BUF_XSIZE * TILEX - x, y - 2 * TILEY,
89                SX, SY + MAX_BUF_YSIZE * TILEY - y);
90     BlitBitmap(screenBitmap, target_bitmap, 0, 0,
91                x - 2 * TILEX, y - 2 * TILEY,
92                SX + MAX_BUF_XSIZE * TILEX - x, SY + MAX_BUF_YSIZE * TILEY - y);
93   }
94 }
95
96 void blitscreen(void)
97 {
98   static boolean scrolling_last = FALSE;
99   int left = screen_x / TILEX;
100   int top  = screen_y / TILEY;
101   boolean scrolling = (screen_x % TILEX != 0 || screen_y % TILEY != 0);
102   int x, y;
103
104   if (redraw_tiles > REDRAWTILES_THRESHOLD || scrolling || scrolling_last)
105   {
106     /* blit all (up to four) parts of the scroll buffer to the backbuffer */
107     BlitScreenToBitmap_EM(backbuffer);
108
109     /* blit the completely updated backbuffer to the window (in one blit) */
110     BlitBitmap(backbuffer, window, SX, SY, SXSIZE, SYSIZE, SX, SY);
111   }
112   else
113   {
114     for (x = 0; x < SCR_FIELDX; x++)
115     {
116       for (y = 0; y < SCR_FIELDY; y++)
117       {
118         int xx = (left + x) % MAX_BUF_XSIZE;
119         int yy = (top  + y) % MAX_BUF_YSIZE;
120
121         if (redraw[xx][yy])
122           BlitBitmap(screenBitmap, window,
123                      xx * TILEX, yy * TILEY, TILEX, TILEY,
124                      SX + x * TILEX, SY + y * TILEY);
125       }
126     }
127   }
128
129   for (x = 0; x < MAX_BUF_XSIZE; x++)
130     for (y = 0; y < MAX_BUF_YSIZE; y++)
131       redraw[x][y] = FALSE;
132   redraw_tiles = 0;
133
134   scrolling_last = scrolling;
135 }
136
137 static void DrawLevelField_EM(int x, int y, int sx, int sy,
138                               boolean draw_masked)
139 {
140   int tile = Draw[y][x];
141   struct GraphicInfo_EM *g = &graphic_info_em_object[tile][frame];
142   int src_x = g->src_x + g->src_offset_x;
143   int src_y = g->src_y + g->src_offset_y;
144   int dst_x = sx * TILEX + g->dst_offset_x;
145   int dst_y = sy * TILEY + g->dst_offset_y;
146   int width = g->width;
147   int height = g->height;
148   int left = screen_x / TILEX;
149   int top  = screen_y / TILEY;
150
151   /* do not draw fields that are outside the visible screen area */
152   if (x < left || x >= left + MAX_BUF_XSIZE ||
153       y < top  || y >= top  + MAX_BUF_YSIZE)
154     return;
155
156   if (draw_masked)
157   {
158     if (width > 0 && height > 0)
159     {
160       SetClipOrigin(g->bitmap, g->bitmap->stored_clip_gc,
161                     dst_x - src_x, dst_y - src_y);
162       BlitBitmapMasked(g->bitmap, screenBitmap,
163                        src_x, src_y, width, height, dst_x, dst_y);
164     }
165   }
166   else
167   {
168     if ((width != TILEX || height != TILEY) && !g->preserve_background)
169       ClearRectangle(screenBitmap, sx * TILEX, sy * TILEY, TILEX, TILEY);
170
171     if (width > 0 && height > 0)
172       BlitBitmap(g->bitmap, screenBitmap,
173                  src_x, src_y, width, height, dst_x, dst_y);
174   }
175 }
176
177 static void DrawLevelFieldCrumbled_EM(int x, int y, int sx, int sy,
178                                       int crm, boolean draw_masked)
179 {
180   int tile = Draw[y][x];
181   struct GraphicInfo_EM *g = &graphic_info_em_object[tile][frame];
182   int left = screen_x / TILEX;
183   int top  = screen_y / TILEY;
184   int i;
185
186   /* do not draw fields that are outside the visible screen area */
187   if (x < left || x >= left + MAX_BUF_XSIZE ||
188       y < top  || y >= top  + MAX_BUF_YSIZE)
189     return;
190
191   if (crm == 0)         /* no crumbled edges for this tile */
192     return;
193
194   for (i = 0; i < 4; i++)
195   {
196     if (crm & (1 << i))
197     {
198       int width, height, cx, cy;
199
200       if (i == 1 || i == 2)
201       {
202         width = g->crumbled_border_size;
203         height = TILEY;
204         cx = (i == 2 ? TILEX - g->crumbled_border_size : 0);
205         cy = 0;
206       }
207       else
208       {
209         width = TILEX;
210         height = g->crumbled_border_size;
211         cx = 0;
212         cy = (i == 3 ? TILEY - g->crumbled_border_size : 0);
213       }
214
215       if (width > 0 && height > 0)
216       {
217         int src_x = g->crumbled_src_x + cx;
218         int src_y = g->crumbled_src_y + cy;
219         int dst_x = sx * TILEX + cx;
220         int dst_y = sy * TILEY + cy;
221
222         if (draw_masked)
223         {
224           SetClipOrigin(g->crumbled_bitmap, g->crumbled_bitmap->stored_clip_gc,
225                         dst_x - src_x, dst_y - src_y);
226           BlitBitmapMasked(g->crumbled_bitmap, screenBitmap,
227                            src_x, src_y, width, height, dst_x, dst_y);
228         }
229         else
230           BlitBitmap(g->crumbled_bitmap, screenBitmap,
231                      src_x, src_y, width, height, dst_x, dst_y);
232       }
233     }
234   }
235 }
236
237 static void DrawLevelPlayer_EM(int x1, int y1, int player_nr, int anim,
238                                boolean draw_masked)
239 {
240   struct GraphicInfo_EM *g = &graphic_info_em_player[player_nr][anim][frame];
241
242   int src_x = g->src_x, src_y = g->src_y;
243   int dst_x, dst_y;
244
245   /* do not draw fields that are outside the visible screen area */
246   if (x1 < screen_x - TILEX || x1 >= screen_x + MAX_BUF_XSIZE * TILEX ||
247       y1 < screen_y - TILEY || y1 >= screen_y + MAX_BUF_YSIZE * TILEY)
248     return;
249
250   x1 %= MAX_BUF_XSIZE * TILEX;
251   y1 %= MAX_BUF_YSIZE * TILEY;
252
253   if (draw_masked)
254   {
255     /* draw the player to current location */
256     dst_x = x1;
257     dst_y = y1;
258     SetClipOrigin(g->bitmap, g->bitmap->stored_clip_gc,
259                   dst_x - src_x, dst_y - src_y);
260     BlitBitmapMasked(g->bitmap, screenBitmap,
261                      src_x, src_y, TILEX, TILEY, dst_x, dst_y);
262
263     /* draw the player to opposite wrap-around column */
264     dst_x = x1 - MAX_BUF_XSIZE * TILEX;
265     dst_y = y1;
266     SetClipOrigin(g->bitmap, g->bitmap->stored_clip_gc,
267                   dst_x - src_x, dst_y - src_y);
268     BlitBitmapMasked(g->bitmap, screenBitmap,
269                      g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
270
271     /* draw the player to opposite wrap-around row */
272     dst_x = x1;
273     dst_y = y1 - MAX_BUF_YSIZE * TILEY;
274     SetClipOrigin(g->bitmap, g->bitmap->stored_clip_gc,
275                   dst_x - src_x, dst_y - src_y);
276     BlitBitmapMasked(g->bitmap, screenBitmap,
277                      g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
278   }
279   else
280   {
281     /* draw the player to current location */
282     dst_x = x1;
283     dst_y = y1;
284     BlitBitmap(g->bitmap, screenBitmap,
285                g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
286
287     /* draw the player to opposite wrap-around column */
288     dst_x = x1 - MAX_BUF_XSIZE * TILEX;
289     dst_y = y1;
290     BlitBitmap(g->bitmap, screenBitmap,
291                g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
292
293     /* draw the player to opposite wrap-around row */
294     dst_x = x1;
295     dst_y = y1 - MAX_BUF_YSIZE * TILEY;
296     BlitBitmap(g->bitmap, screenBitmap,
297                g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
298   }
299 }
300
301 /* draw differences between game tiles and screen tiles
302  *
303  * implicitly handles scrolling and restoring background under the sprites
304  */
305
306 static void animscreen(void)
307 {
308   int x, y, i;
309   int left = screen_x / TILEX;
310   int top  = screen_y / TILEY;
311   static int xy[4][2] =
312   {
313     { 0, -1 },
314     { -1, 0 },
315     { +1, 0 },
316     { 0, +1 }
317   };
318
319   for (y = top; y < top + MAX_BUF_YSIZE; y++)
320   {
321     for (x = left; x < left + MAX_BUF_XSIZE; x++)
322     {
323       int sx = x % MAX_BUF_XSIZE;
324       int sy = y % MAX_BUF_YSIZE;    
325       int tile = Draw[y][x];
326       struct GraphicInfo_EM *g = &graphic_info_em_object[tile][frame];
327       int obj = g->unique_identifier;
328       int crm = 0;
329
330       /* re-calculate crumbled state of this tile */
331       if (g->has_crumbled_graphics)
332       {
333         for (i = 0; i < 4; i++)
334         {
335           int xx = x + xy[i][0];
336           int yy = y + xy[i][1];
337           int tile_next;
338
339           if (xx < 0 || xx >= EM_MAX_CAVE_WIDTH ||
340               yy < 0 || yy >= EM_MAX_CAVE_HEIGHT)
341             continue;
342
343           tile_next = Draw[yy][xx];
344
345           if (!graphic_info_em_object[tile_next][frame].has_crumbled_graphics)
346             crm |= (1 << i);
347         }
348       }
349
350       /* only redraw screen tiles if they (or their crumbled state) changed */
351       if (screentiles[sy][sx] != obj || crumbled_state[sy][sx] != crm)
352       {
353         DrawLevelField_EM(x, y, sx, sy, FALSE);
354         DrawLevelFieldCrumbled_EM(x, y, sx, sy, crm, FALSE);
355
356         screentiles[sy][sx] = obj;
357         crumbled_state[sy][sx] = crm;
358
359         redraw[sx][sy] = TRUE;
360         redraw_tiles++;
361       }
362     }
363   }
364 }
365
366
367 /* blit players to the screen
368  *
369  * handles transparency and movement
370  */
371
372 static void blitplayer(struct PLAYER *ply)
373 {
374   int x1, y1, x2, y2;
375
376   if (!ply->alive)
377     return;
378
379   /* x1/y1 are left/top and x2/y2 are right/down part of the player movement */
380   x1 = (frame * ply->oldx + (8 - frame) * ply->x) * TILEX / 8;
381   y1 = (frame * ply->oldy + (8 - frame) * ply->y) * TILEY / 8;
382   x2 = x1 + TILEX - 1;
383   y2 = y1 + TILEY - 1;
384
385   if ((int)(x2 - screen_x) < ((MAX_BUF_XSIZE - 1) * TILEX - 1) &&
386       (int)(y2 - screen_y) < ((MAX_BUF_YSIZE - 1) * TILEY - 1))
387   {
388     /* some casts to "int" are needed because of negative calculation values */
389     int dx = (int)ply->x - (int)ply->oldx;
390     int dy = (int)ply->y - (int)ply->oldy;
391     int old_x = (int)ply->oldx + (7 - (int)frame) * dx / 8;
392     int old_y = (int)ply->oldy + (7 - (int)frame) * dy / 8;
393     int new_x = old_x + SIGN(dx);
394     int new_y = old_y + SIGN(dy);
395     int old_sx = old_x % MAX_BUF_XSIZE;
396     int old_sy = old_y % MAX_BUF_XSIZE;
397     int new_sx = new_x % MAX_BUF_XSIZE;
398     int new_sy = new_y % MAX_BUF_XSIZE;
399 #if 0
400     int old_crm = crumbled_state[old_sy][old_sx];
401 #endif
402     int new_crm = crumbled_state[new_sy][new_sx];
403
404     /* only diggable elements can be crumbled in the classic EM engine */
405     boolean player_is_digging = (new_crm != 0);
406
407 #if 0
408     x1 %= MAX_BUF_XSIZE * TILEX;
409     y1 %= MAX_BUF_YSIZE * TILEY;
410     x2 %= MAX_BUF_XSIZE * TILEX;
411     y2 %= MAX_BUF_YSIZE * TILEY;
412 #endif
413
414     if (player_is_digging)
415     {
416 #if 0
417       /* draw the field the player is moving from (under the player) */
418       DrawLevelField_EM(old_x, old_y, old_sx, old_sy, FALSE);
419       DrawLevelFieldCrumbled_EM(old_x, old_y, old_sx, old_sy, old_crm, FALSE);
420 #endif
421
422       /* draw the field the player is moving to (under the player) */
423       DrawLevelField_EM(new_x, new_y, new_sx, new_sy, FALSE);
424       DrawLevelFieldCrumbled_EM(new_x, new_y, new_sx, new_sy, new_crm, FALSE);
425
426       /* draw the player (masked) over the element he is just digging away */
427       DrawLevelPlayer_EM(x1, y1, ply->num, ply->anim, TRUE);
428
429 #if 1
430       /* draw the field the player is moving from (masked over the player) */
431       DrawLevelField_EM(old_x, old_y, old_sx, old_sy, TRUE);
432 #endif
433     }
434     else
435     {
436       /* draw the player under the element which is on the same field */
437       DrawLevelPlayer_EM(x1, y1, ply->num, ply->anim, FALSE);
438
439       /* draw the field the player is moving from (masked over the player) */
440       DrawLevelField_EM(old_x, old_y, old_sx, old_sy, TRUE);
441
442       /* draw the field the player is moving to (masked over the player) */
443       DrawLevelField_EM(new_x, new_y, new_sx, new_sy, TRUE);
444     }
445
446     /* mark screen tiles as dirty */
447     screentiles[old_sy][old_sx] = -1;
448     screentiles[new_sy][new_sx] = -1;
449   }
450 }
451
452 void game_initscreen(void)
453 {
454   int x,y;
455   int dynamite_state = ply[0].dynamite;         /* !!! ONLY PLAYER 1 !!! */
456   int all_keys_state = ply[0].keys | ply[1].keys | ply[2].keys | ply[3].keys;
457   int player_nr;
458
459   frame = 6;
460
461   centered_player_nr = getCenteredPlayerNr_EM();
462
463   player_nr = (centered_player_nr != -1 ? centered_player_nr : 0);
464
465   screen_x = VALID_SCREEN_X(PLAYER_SCREEN_X(player_nr));
466   screen_y = VALID_SCREEN_Y(PLAYER_SCREEN_Y(player_nr));
467
468   for (y = 0; y < MAX_BUF_YSIZE; y++)
469   {
470     for (x = 0; x < MAX_BUF_XSIZE; x++)
471     {
472       screentiles[y][x] = -1;
473       crumbled_state[y][x] = 0;
474     }
475   }
476
477   DrawAllGameValues(lev.required, dynamite_state, lev.score,
478                     lev.time, all_keys_state);
479 }
480
481 #if 0
482 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
483 {
484   boolean ffwd_delay = (tape.playing && tape.fast_forward);
485   boolean no_delay = (tape.warp_forward);
486   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
487   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
488   int jx = player->jx;
489   int jy = player->jy;
490
491   if (quick_relocation)
492   {
493     int offset = (setup.scroll_delay ? 3 : 0);
494
495     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
496     {
497       scroll_x = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
498                   player->jx > SBX_Right + MIDPOSX ? SBX_Right :
499                   player->jx - MIDPOSX);
500
501       scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
502                   player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
503                   player->jy - MIDPOSY);
504     }
505     else
506     {
507       if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
508           (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
509         scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
510
511       if ((player->MovDir == MV_UP  && scroll_y > jy - MIDPOSY + offset) ||
512           (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
513         scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
514
515       /* don't scroll over playfield boundaries */
516       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
517         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
518
519       /* don't scroll over playfield boundaries */
520       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
521         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
522     }
523
524     RedrawPlayfield(TRUE, 0,0,0,0);
525   }
526   else
527   {
528     int scroll_xx = -999, scroll_yy = -999;
529
530     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
531
532     while (scroll_xx != scroll_x || scroll_yy != scroll_y)
533     {
534       int dx = 0, dy = 0;
535       int fx = FX, fy = FY;
536
537       scroll_xx = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
538                    player->jx > SBX_Right + MIDPOSX ? SBX_Right :
539                    player->jx - MIDPOSX);
540
541       scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
542                    player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
543                    player->jy - MIDPOSY);
544
545       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
546       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
547
548       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
549         break;
550
551       scroll_x -= dx;
552       scroll_y -= dy;
553
554       fx += dx * TILEX / 2;
555       fy += dy * TILEY / 2;
556
557       ScrollLevel(dx, dy);
558       DrawAllPlayers();
559
560       /* scroll in two steps of half tile size to make things smoother */
561       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
562       FlushDisplay();
563       Delay(wait_delay_value);
564
565       /* scroll second step to align at full tile size */
566       BackToFront();
567       Delay(wait_delay_value);
568     }
569
570     DrawPlayer(player);
571     BackToFront();
572     Delay(wait_delay_value);
573   }
574 }
575 #endif
576
577 void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
578 {
579   boolean num_checked_players = 0;
580   int i;
581
582   for (i = 0; i < MAX_PLAYERS; i++)
583   {
584     if (ply[i].alive)
585     {
586       int sx = PLAYER_SCREEN_X(i);
587       int sy = PLAYER_SCREEN_Y(i);
588
589       if (num_checked_players == 0)
590       {
591         *sx1 = *sx2 = sx;
592         *sy1 = *sy2 = sy;
593       }
594       else
595       {
596         *sx1 = MIN(*sx1, sx);
597         *sy1 = MIN(*sy1, sy);
598         *sx2 = MAX(*sx2, sx);
599         *sy2 = MAX(*sy2, sy);
600       }
601
602       num_checked_players++;
603     }
604   }
605 }
606
607 boolean checkIfAllPlayersFitToScreen()
608 {
609   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
610
611   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
612
613   return (sx2 - sx1 <= SCR_FIELDX * TILEX &&
614           sy2 - sy1 <= SCR_FIELDY * TILEY);
615 }
616
617 void setScreenCenteredToAllPlayers(int *sx, int *sy)
618 {
619   int sx1 = screen_x, sy1 = screen_y, sx2 = screen_x, sy2 = screen_y;
620
621   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
622
623   *sx = (sx1 + sx2) / 2;
624   *sy = (sy1 + sy2) / 2;
625 }
626
627 void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
628                                        int center_x, int center_y)
629 {
630   int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
631
632   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
633
634   *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
635   *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
636 }
637
638 boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
639 {
640   int max_dx, max_dy;
641
642   setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
643
644   return (max_dx <= SCR_FIELDX * TILEX / 2 &&
645           max_dy <= SCR_FIELDY * TILEY / 2);
646 }
647
648 void RedrawPlayfield_EM(boolean force_redraw)
649 {
650 #if 0
651   boolean all_players_visible = checkIfAllPlayersAreVisible();
652 #endif
653   boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen();
654   boolean draw_new_player_location = FALSE;
655   boolean quick_relocation = setup.quick_switch;
656 #if 0
657   boolean scrolling = (screen_x % TILEX != 0 || screen_y % TILEY != 0);
658 #endif
659   int centered_player_nr_next = getCenteredPlayerNr_EM();
660   int offset = (setup.scroll_delay ? 3 : 0) * TILEX;
661   int offset_x = offset;
662   int offset_y = offset;
663   int screen_x_old = screen_x;
664   int screen_y_old = screen_y;
665   int x, y, sx, sy;
666   int i;
667
668   /* switching to "all players" only possible if all players fit to screen */
669   if (centered_player_nr_next == -1 && !all_players_fit_to_screen)
670   {
671     setCenteredPlayerNr_EM(centered_player_nr);
672
673     centered_player_nr_next = centered_player_nr;
674   }
675
676 #if 1
677   /* also allow focus switching when screen is scrolled to half tile */
678 #else
679   if (!scrolling)       /* screen currently aligned at tile position */
680 #endif
681   {
682     if (centered_player_nr != centered_player_nr_next)
683     {
684       centered_player_nr = centered_player_nr_next;
685
686       draw_new_player_location = TRUE;
687       force_redraw = TRUE;
688     }
689   }
690
691   if (centered_player_nr == -1)
692   {
693     if (draw_new_player_location)
694     {
695       setScreenCenteredToAllPlayers(&sx, &sy);
696     }
697     else
698     {
699       sx = PLAYER_SCREEN_X(game_em.last_moving_player);
700       sy = PLAYER_SCREEN_Y(game_em.last_moving_player);
701     }
702   }
703   else
704   {
705     sx = PLAYER_SCREEN_X(centered_player_nr);
706     sy = PLAYER_SCREEN_Y(centered_player_nr);
707   }
708
709   if (draw_new_player_location && !quick_relocation)
710   {
711 #if 1
712     unsigned long game_frame_delay_value = getGameFrameDelay_EM(20);
713 #else
714     unsigned long game_frame_delay_value = getGameFrameDelay_EM(25);
715 #endif
716     int wait_delay_value = game_frame_delay_value;
717     int screen_xx = VALID_SCREEN_X(sx);
718     int screen_yy = VALID_SCREEN_Y(sy);
719
720     while (screen_x != screen_xx || screen_y != screen_yy)
721     {
722       int dx = (screen_xx < screen_x ? +1 : screen_xx > screen_x ? -1 : 0);
723       int dy = (screen_yy < screen_y ? +1 : screen_yy > screen_y ? -1 : 0);
724       int dxx = 0, dyy = 0;
725
726       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
727         break;
728
729 #if 1
730
731       if (ABS(screen_xx - screen_x) >= TILEX)
732       {
733         screen_x -= dx * TILEX;
734         dxx = dx * TILEX / 2;
735       }
736       else
737       {
738         screen_x = screen_xx;
739         dxx = 0;
740       }
741
742       if (ABS(screen_yy - screen_y) >= TILEY)
743       {
744         screen_y -= dy * TILEY;
745         dyy = dy * TILEY / 2;
746       }
747       else
748       {
749         screen_y = screen_yy;
750         dyy = 0;
751       }
752
753 #else
754
755 #if 1
756       if (ABS(screen_xx - screen_x) >= TILEX ||
757           ABS(screen_yy - screen_y) >= TILEY)
758       {
759         screen_x -= dx * TILEX;
760         screen_y -= dy * TILEY;
761
762         dxx = dx * TILEX / 2;
763         dyy = dy * TILEY / 2;
764       }
765       else
766       {
767         screen_x = screen_xx;
768         screen_y = screen_yy;
769
770         dxx = 0;
771         dyy = 0;
772       }
773 #else
774       screen_x -= dx * TILEX;
775       screen_y -= dy * TILEY;
776
777       dxx += dx * TILEX / 2;
778       dyy += dy * TILEY / 2;
779 #endif
780
781 #endif
782
783       /* scroll in two steps of half tile size to make things smoother */
784       screen_x += dxx;
785       screen_y += dyy;
786
787       animscreen();
788
789       for (i = 0; i < MAX_PLAYERS; i++)
790         blitplayer(&ply[i]);
791
792       blitscreen();
793       FlushDisplay();
794       Delay(wait_delay_value);
795
796       /* scroll second step to align at full tile size */
797       screen_x -= dxx;
798       screen_y -= dyy;
799
800       SyncDisplay();
801
802       animscreen();
803
804       for (i = 0; i < MAX_PLAYERS; i++)
805         blitplayer(&ply[i]);
806
807       blitscreen();
808       FlushDisplay();
809       Delay(wait_delay_value);
810     }
811
812     screen_x_old = screen_x;
813     screen_y_old = screen_y;
814   }
815
816   if (force_redraw)
817   {
818     for (y = 0; y < MAX_BUF_YSIZE; y++)
819     {
820       for (x = 0; x < MAX_BUF_XSIZE; x++)
821       {
822         screentiles[y][x] = -1;
823         crumbled_state[y][x] = 0;
824       }
825     }
826   }
827
828   /* calculate new screen scrolling position, with regard to scroll delay */
829   screen_x = VALID_SCREEN_X(sx + offset_x < screen_x ? sx + offset_x :
830                             sx - offset_x > screen_x ? sx - offset_x :
831                             screen_x);
832   screen_y = VALID_SCREEN_Y(sy + offset_y < screen_y ? sy + offset_y :
833                             sy - offset_y > screen_y ? sy - offset_y :
834                             screen_y);
835
836   /* prevent scrolling further than player step size screen when scrolling */
837   if (ABS(screen_x - screen_x_old) > TILEX / 8 ||
838       ABS(screen_y - screen_y_old) > TILEY / 8)
839   {
840     int dx = SIGN(screen_x - screen_x_old);
841     int dy = SIGN(screen_y - screen_y_old);
842
843     screen_x = screen_x_old + dx * TILEX / 8;
844     screen_y = screen_y_old + dy * TILEY / 8;
845   }
846
847   /* prevent scrolling away from the other players when focus on all players */
848   if (centered_player_nr == -1)
849   {
850 #if 1
851     /* check if all players are still visible with new scrolling position */
852     if (!checkIfAllPlayersAreVisible(screen_x, screen_y))
853     {
854       /* reset horizontal scroll position to last value, if needed */
855       if (!checkIfAllPlayersAreVisible(screen_x, screen_y_old))
856         screen_x = screen_x_old;
857
858       /* reset vertical scroll position to last value, if needed */
859       if (!checkIfAllPlayersAreVisible(screen_x_old, screen_y))
860         screen_y = screen_y_old;
861     }
862 #else
863     boolean all_players_visible = checkIfAllPlayersAreVisible();
864
865     if (!all_players_visible)
866     {
867       printf("::: not all players visible\n");
868
869       screen_x = screen_x_old;
870       screen_y = screen_y_old;
871     }
872 #endif
873   }
874
875   /* prevent scrolling (for screen correcting) if no player is moving */
876   if (!game_em.any_player_moving)
877   {
878     screen_x = screen_x_old;
879     screen_y = screen_y_old;
880   }
881   else
882   {
883     /* prevent scrolling against the players move direction */
884     int player_nr = game_em.last_moving_player;
885     int player_move_dir = game_em.last_player_direction[player_nr];
886     int dx = SIGN(screen_x - screen_x_old);
887     int dy = SIGN(screen_y - screen_y_old);
888
889     if ((dx < 0 && player_move_dir != MV_LEFT) ||
890         (dx > 0 && player_move_dir != MV_RIGHT))
891       screen_x = screen_x_old;
892
893     if ((dy < 0 && player_move_dir != MV_UP) ||
894         (dy > 0 && player_move_dir != MV_DOWN))
895       screen_y = screen_y_old;
896   }
897
898   animscreen();
899
900   for (i = 0; i < MAX_PLAYERS; i++)
901     blitplayer(&ply[i]);
902
903   SyncDisplay();
904
905   blitscreen();
906
907   FlushDisplay();
908 }
909
910 void game_animscreen(void)
911 {
912   RedrawPlayfield_EM(FALSE);
913 }
914
915 void DrawGameDoorValues_EM()
916 {
917   int dynamite_state = ply[0].dynamite;         /* !!! ONLY PLAYER 1 !!! */
918   int all_keys_state = ply[0].keys | ply[1].keys | ply[2].keys | ply[3].keys;
919
920 #if 1
921   DrawAllGameValues(lev.required, dynamite_state, lev.score,
922                     lev.time, all_keys_state);
923 #else
924   DrawAllGameValues(lev.required, ply1.dynamite, lev.score,
925                     DISPLAY_TIME(lev.time), ply1.keys | ply2.keys);
926 #endif
927 }