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