increased cave buffer offset (and adjusted graphics and amoeba logic)
[rocksndiamonds.git] / src / game_em / graphics.c
1 /* 2000-08-13T14:36:17Z
2  *
3  * graphics manipulation crap
4  */
5
6 #include "main_em.h"
7
8 #define MIN_SCREEN_XPOS_RAW     0
9 #define MIN_SCREEN_YPOS_RAW     0
10 #define MAX_SCREEN_XPOS_RAW     MAX(0, lev.width  - SCR_FIELDX)
11 #define MAX_SCREEN_YPOS_RAW     MAX(0, lev.height - SCR_FIELDY)
12
13 #define MIN_SCREEN_XPOS         (MIN_SCREEN_XPOS_RAW + CAVE_BUFFER_XOFFSET)
14 #define MIN_SCREEN_YPOS         (MIN_SCREEN_YPOS_RAW + CAVE_BUFFER_YOFFSET)
15 #define MAX_SCREEN_XPOS         (MAX_SCREEN_XPOS_RAW + CAVE_BUFFER_XOFFSET)
16 #define MAX_SCREEN_YPOS         (MAX_SCREEN_YPOS_RAW + CAVE_BUFFER_YOFFSET)
17
18 #define MIN_SCREEN_X            (MIN_SCREEN_XPOS * TILEX)
19 #define MIN_SCREEN_Y            (MIN_SCREEN_YPOS * TILEY)
20 #define MAX_SCREEN_X            (MAX_SCREEN_XPOS * TILEX)
21 #define MAX_SCREEN_Y            (MAX_SCREEN_YPOS * TILEY)
22
23 #define VALID_SCREEN_X(x)       ((x) < MIN_SCREEN_X ? MIN_SCREEN_X :    \
24                                  (x) > MAX_SCREEN_X ? MAX_SCREEN_X : (x))
25 #define VALID_SCREEN_Y(y)       ((y) < MIN_SCREEN_Y ? MIN_SCREEN_Y :    \
26                                  (y) > MAX_SCREEN_Y ? MAX_SCREEN_Y : (y))
27
28 #define PLAYER_SCREEN_X(p)      (((    frame) * ply[p].oldx +           \
29                                   (8 - frame) * ply[p].x) * TILEX / 8   \
30                                  - ((SCR_FIELDX - 1) * TILEX) / 2)
31 #define PLAYER_SCREEN_Y(p)      (((    frame) * ply[p].oldy +           \
32                                   (8 - frame) * ply[p].y) * TILEY / 8   \
33                                  - ((SCR_FIELDY - 1) * TILEY) / 2)
34
35 #define USE_EXTENDED_GRAPHICS_ENGINE            1
36
37
38 int frame;                              /* current screen frame */
39 int screen_x, screen_y;                 /* current scroll position */
40
41 /* tiles currently on screen */
42 static int screentiles[MAX_PLAYFIELD_WIDTH + 2][MAX_PLAYFIELD_HEIGHT + 2];
43 static int crumbled_state[MAX_PLAYFIELD_WIDTH + 2][MAX_PLAYFIELD_HEIGHT + 2];
44
45 int getFieldbufferOffsetX_EM(void)
46 {
47   return screen_x % TILEX;
48 }
49
50 int getFieldbufferOffsetY_EM(void)
51 {
52   return screen_y % TILEY;
53 }
54
55 void BlitScreenToBitmap_EM(Bitmap *target_bitmap)
56 {
57   /* blit all (up to four) parts of the scroll buffer to the target bitmap */
58
59   int x = screen_x % (MAX_BUF_XSIZE * TILEX);
60   int y = screen_y % (MAX_BUF_YSIZE * TILEY);
61   int xsize = SXSIZE;
62   int ysize = SYSIZE;
63   int full_xsize = lev.width  * TILEX;
64   int full_ysize = lev.height * TILEY;
65   int sx = SX + (full_xsize < xsize ? (xsize - full_xsize) / 2 : 0);
66   int sy = SY + (full_ysize < ysize ? (ysize - full_ysize) / 2 : 0);
67   int sxsize = (full_xsize < xsize ? full_xsize : xsize);
68   int sysize = (full_ysize < ysize ? full_ysize : ysize);
69
70   if (x < 2 * TILEX && y < 2 * TILEY)
71   {
72     BlitBitmap(screenBitmap, target_bitmap, x, y,
73                sxsize, sysize, sx, sy);
74   }
75   else if (x < 2 * TILEX && y >= 2 * TILEY)
76   {
77     BlitBitmap(screenBitmap, target_bitmap, x, y,
78                sxsize, MAX_BUF_YSIZE * TILEY - y,
79                sx, sy);
80     BlitBitmap(screenBitmap, target_bitmap, x, 0,
81                sxsize, y - 2 * TILEY,
82                sx, sy + MAX_BUF_YSIZE * TILEY - y);
83   }
84   else if (x >= 2 * TILEX && y < 2 * TILEY)
85   {
86     BlitBitmap(screenBitmap, target_bitmap, x, y,
87                MAX_BUF_XSIZE * TILEX - x, sysize,
88                sx, sy);
89     BlitBitmap(screenBitmap, target_bitmap, 0, y,
90                x - 2 * TILEX, sysize,
91                sx + MAX_BUF_XSIZE * TILEX - x, sy);
92   }
93   else
94   {
95     BlitBitmap(screenBitmap, target_bitmap, x, y,
96                MAX_BUF_XSIZE * TILEX - x, MAX_BUF_YSIZE * TILEY - y,
97                sx, sy);
98     BlitBitmap(screenBitmap, target_bitmap, 0, y,
99                x - 2 * TILEX, MAX_BUF_YSIZE * TILEY - y,
100                sx + MAX_BUF_XSIZE * TILEX - x, sy);
101     BlitBitmap(screenBitmap, target_bitmap, x, 0,
102                MAX_BUF_XSIZE * TILEX - x, y - 2 * TILEY,
103                sx, sy + MAX_BUF_YSIZE * TILEY - y);
104     BlitBitmap(screenBitmap, target_bitmap, 0, 0,
105                x - 2 * TILEX, y - 2 * TILEY,
106                sx + MAX_BUF_XSIZE * TILEX - x, sy + MAX_BUF_YSIZE * TILEY - y);
107   }
108 }
109
110 void BackToFront_EM(void)
111 {
112   BlitBitmap(backbuffer, window, SX, SY, SXSIZE, SYSIZE, SX, SY);
113 }
114
115 static struct GraphicInfo_EM *getObjectGraphic(int x, int y)
116 {
117   int tile = lev.draw[x][y];
118   struct GraphicInfo_EM *g = &graphic_info_em_object[tile][frame];
119
120   if (!game.use_native_emc_graphics_engine)
121     getGraphicSourceObjectExt_EM(g, tile, 7 - frame, x - 2, y - 2);
122
123   return g;
124 }
125
126 static struct GraphicInfo_EM *getPlayerGraphic(int player_nr, int anim)
127 {
128   struct GraphicInfo_EM *g = &graphic_info_em_player[player_nr][anim][frame];
129
130   if (!game.use_native_emc_graphics_engine)
131     getGraphicSourcePlayerExt_EM(g, player_nr, anim, 7 - frame);
132
133   return g;
134 }
135
136 static void DrawLevelField_EM(int x, int y, int sx, int sy,
137                               boolean draw_masked)
138 {
139   struct GraphicInfo_EM *g = getObjectGraphic(x, y);
140   int src_x = g->src_x + g->src_offset_x * TILESIZE_VAR / TILESIZE;
141   int src_y = g->src_y + g->src_offset_y * TILESIZE_VAR / TILESIZE;
142   int dst_x = sx * TILEX + g->dst_offset_x * TILESIZE_VAR / TILESIZE;
143   int dst_y = sy * TILEY + g->dst_offset_y * TILESIZE_VAR / TILESIZE;
144   int width = g->width * TILESIZE_VAR / TILESIZE;
145   int height = g->height * TILESIZE_VAR / TILESIZE;
146   int left = screen_x / TILEX;
147   int top  = screen_y / TILEY;
148
149   /* do not draw fields that are outside the visible screen area */
150   if (x < left || x >= left + MAX_BUF_XSIZE ||
151       y < top  || y >= top  + MAX_BUF_YSIZE)
152     return;
153
154   if (draw_masked)
155   {
156     if (width > 0 && height > 0)
157       BlitBitmapMasked(g->bitmap, screenBitmap,
158                        src_x, src_y, width, height, dst_x, dst_y);
159   }
160   else
161   {
162     if ((width != TILEX || height != TILEY) && !g->preserve_background)
163       ClearRectangle(screenBitmap, sx * TILEX, sy * TILEY, TILEX, TILEY);
164
165     if (width > 0 && height > 0)
166       BlitBitmap(g->bitmap, screenBitmap,
167                  src_x, src_y, width, height, dst_x, dst_y);
168   }
169 }
170
171 static void DrawLevelFieldCrumbled_EM(int x, int y, int sx, int sy,
172                                       int crm, boolean draw_masked)
173 {
174   struct GraphicInfo_EM *g;
175   int crumbled_border_size;
176   int left = screen_x / TILEX;
177   int top  = screen_y / TILEY;
178   int i;
179
180   /* do not draw fields that are outside the visible screen area */
181   if (x < left || x >= left + MAX_BUF_XSIZE ||
182       y < top  || y >= top  + MAX_BUF_YSIZE)
183     return;
184
185   if (crm == 0)         /* no crumbled edges for this tile */
186     return;
187
188   g = getObjectGraphic(x, y);
189
190   crumbled_border_size =
191     g->crumbled_border_size * TILESIZE_VAR / g->crumbled_tile_size;
192
193   for (i = 0; i < 4; i++)
194   {
195     if (crm & (1 << i))
196     {
197       int width, height, cx, cy;
198
199       if (i == 1 || i == 2)
200       {
201         width = crumbled_border_size;
202         height = TILEY;
203         cx = (i == 2 ? TILEX - crumbled_border_size : 0);
204         cy = 0;
205       }
206       else
207       {
208         width = TILEX;
209         height = crumbled_border_size;
210         cx = 0;
211         cy = (i == 3 ? TILEY - crumbled_border_size : 0);
212       }
213
214       if (width > 0 && height > 0)
215       {
216         int src_x = g->crumbled_src_x + cx;
217         int src_y = g->crumbled_src_y + cy;
218         int dst_x = sx * TILEX + cx;
219         int dst_y = sy * TILEY + cy;
220
221         if (draw_masked)
222           BlitBitmapMasked(g->crumbled_bitmap, screenBitmap,
223                            src_x, src_y, width, height, dst_x, dst_y);
224         else
225           BlitBitmap(g->crumbled_bitmap, screenBitmap,
226                      src_x, src_y, width, height, dst_x, dst_y);
227       }
228     }
229   }
230 }
231
232 static void DrawLevelPlayer_EM(int x1, int y1, int player_nr, int anim,
233                                boolean draw_masked)
234 {
235   struct GraphicInfo_EM *g = getPlayerGraphic(player_nr, anim);
236   int src_x = g->src_x, src_y = g->src_y;
237   int dst_x, dst_y;
238
239   /* do not draw fields that are outside the visible screen area */
240   if (x1 < screen_x - TILEX || x1 >= screen_x + MAX_BUF_XSIZE * TILEX ||
241       y1 < screen_y - TILEY || y1 >= screen_y + MAX_BUF_YSIZE * TILEY)
242     return;
243
244   x1 %= MAX_BUF_XSIZE * TILEX;
245   y1 %= MAX_BUF_YSIZE * TILEY;
246
247   if (draw_masked)
248   {
249     /* draw the player to current location */
250     dst_x = x1;
251     dst_y = y1;
252     BlitBitmapMasked(g->bitmap, screenBitmap,
253                      src_x, src_y, TILEX, TILEY, dst_x, dst_y);
254
255     /* draw the player to opposite wrap-around column */
256     dst_x = x1 - MAX_BUF_XSIZE * TILEX;
257     dst_y = y1;
258     BlitBitmapMasked(g->bitmap, screenBitmap,
259                      g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
260
261     /* draw the player to opposite wrap-around row */
262     dst_x = x1;
263     dst_y = y1 - MAX_BUF_YSIZE * TILEY;
264     BlitBitmapMasked(g->bitmap, screenBitmap,
265                      g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
266   }
267   else
268   {
269     /* draw the player to current location */
270     dst_x = x1;
271     dst_y = y1;
272     BlitBitmap(g->bitmap, screenBitmap,
273                g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
274
275     /* draw the player to opposite wrap-around column */
276     dst_x = x1 - MAX_BUF_XSIZE * TILEX;
277     dst_y = y1;
278     BlitBitmap(g->bitmap, screenBitmap,
279                g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
280
281     /* draw the player to opposite wrap-around row */
282     dst_x = x1;
283     dst_y = y1 - MAX_BUF_YSIZE * TILEY;
284     BlitBitmap(g->bitmap, screenBitmap,
285                g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
286   }
287 }
288
289 /* draw differences between game tiles and screen tiles
290  *
291  * implicitly handles scrolling and restoring background under the sprites
292  */
293
294 static void animscreen(void)
295 {
296   int x, y, i;
297   int left = screen_x / TILEX;
298   int top  = screen_y / TILEY;
299   static int xy[4][2] =
300   {
301     { 0, -1 },
302     { -1, 0 },
303     { +1, 0 },
304     { 0, +1 }
305   };
306
307   if (!game.use_native_emc_graphics_engine)
308     for (y = 2; y < CAVE_BUFFER_HEIGHT - 2; y++)
309       for (x = 2; x < CAVE_BUFFER_WIDTH - 2; x++)
310         SetGfxAnimation_EM(&graphic_info_em_object[lev.draw[x][y]][frame],
311                            lev.draw[x][y], 7 - frame, x - 2, y - 2);
312
313   for (y = top; y < top + MAX_BUF_YSIZE; y++)
314   {
315     for (x = left; x < left + MAX_BUF_XSIZE; x++)
316     {
317       int sx = x % MAX_BUF_XSIZE;
318       int sy = y % MAX_BUF_YSIZE;    
319       int tile = lev.draw[x][y];
320       struct GraphicInfo_EM *g = &graphic_info_em_object[tile][frame];
321       int obj = g->unique_identifier;
322       int crm = 0;
323       boolean redraw_screen_tile = FALSE;
324
325       /* re-calculate crumbled state of this tile */
326       if (g->has_crumbled_graphics)
327       {
328         for (i = 0; i < 4; i++)
329         {
330           int xx = x + xy[i][0];
331           int yy = y + xy[i][1];
332           int tile_next;
333
334           if (xx < 0 || xx >= CAVE_BUFFER_WIDTH ||
335               yy < 0 || yy >= CAVE_BUFFER_HEIGHT)
336             continue;
337
338           tile_next = lev.draw[xx][yy];
339
340           if (!graphic_info_em_object[tile_next][frame].has_crumbled_graphics)
341             crm |= (1 << i);
342         }
343       }
344
345       redraw_screen_tile = (screentiles[sx][sy]    != obj ||
346                             crumbled_state[sx][sy] != crm);
347
348       /* only redraw screen tiles if they (or their crumbled state) changed */
349       if (redraw_screen_tile)
350       {
351         DrawLevelField_EM(x, y, sx, sy, FALSE);
352         DrawLevelFieldCrumbled_EM(x, y, sx, sy, crm, FALSE);
353
354         screentiles[sx][sy] = obj;
355         crumbled_state[sx][sy] = crm;
356       }
357     }
358   }
359 }
360
361
362 /* blit players to the screen
363  *
364  * handles transparency and movement
365  */
366
367 static void blitplayer(struct PLAYER *ply)
368 {
369   int x1, y1, x2, y2;
370
371   if (!ply->alive)
372     return;
373
374   /* x1/y1 are left/top and x2/y2 are right/down part of the player movement */
375   x1 = (frame * ply->oldx + (8 - frame) * ply->x) * TILEX / 8;
376   y1 = (frame * ply->oldy + (8 - frame) * ply->y) * TILEY / 8;
377   x2 = x1 + TILEX - 1;
378   y2 = y1 + TILEY - 1;
379
380   if ((int)(x2 - screen_x) < ((MAX_BUF_XSIZE - 1) * TILEX - 1) &&
381       (int)(y2 - screen_y) < ((MAX_BUF_YSIZE - 1) * TILEY - 1))
382   {
383     /* some casts to "int" are needed because of negative calculation values */
384     int dx = (int)ply->x - (int)ply->oldx;
385     int dy = (int)ply->y - (int)ply->oldy;
386     int old_x = (int)ply->oldx + (7 - (int)frame) * dx / 8;
387     int old_y = (int)ply->oldy + (7 - (int)frame) * dy / 8;
388     int new_x = old_x + SIGN(dx);
389     int new_y = old_y + SIGN(dy);
390     int old_sx = old_x % MAX_BUF_XSIZE;
391     int old_sy = old_y % MAX_BUF_YSIZE;
392     int new_sx = new_x % MAX_BUF_XSIZE;
393     int new_sy = new_y % MAX_BUF_YSIZE;
394     int new_crm = crumbled_state[new_sx][new_sy];
395
396     /* only diggable elements can be crumbled in the classic EM engine */
397     boolean player_is_digging = (new_crm != 0);
398
399     if (player_is_digging)
400     {
401       /* draw the field the player is moving to (under the player) */
402       DrawLevelField_EM(new_x, new_y, new_sx, new_sy, FALSE);
403       DrawLevelFieldCrumbled_EM(new_x, new_y, new_sx, new_sy, new_crm, FALSE);
404
405       /* draw the player (masked) over the element he is just digging away */
406       DrawLevelPlayer_EM(x1, y1, ply->num, ply->anim, TRUE);
407
408       /* draw the field the player is moving from (masked over the player) */
409       DrawLevelField_EM(old_x, old_y, old_sx, old_sy, TRUE);
410     }
411     else
412     {
413       /* draw the player under the element which is on the same field */
414       DrawLevelPlayer_EM(x1, y1, ply->num, ply->anim, FALSE);
415
416       /* draw the field the player is moving from (masked over the player) */
417       DrawLevelField_EM(old_x, old_y, old_sx, old_sy, TRUE);
418
419       /* draw the field the player is moving to (masked over the player) */
420       DrawLevelField_EM(new_x, new_y, new_sx, new_sy, TRUE);
421     }
422
423     /* redraw screen tiles in the next frame (player may have left the tiles) */
424     screentiles[old_sx][old_sy] = -1;
425     screentiles[new_sx][new_sy] = -1;
426   }
427 }
428
429 void game_initscreen(void)
430 {
431   int player_nr;
432   int x,y;
433
434   frame = 6;
435
436   player_nr = (game.centered_player_nr != -1 ? game.centered_player_nr : 0);
437
438   screen_x = VALID_SCREEN_X(PLAYER_SCREEN_X(player_nr));
439   screen_y = VALID_SCREEN_Y(PLAYER_SCREEN_Y(player_nr));
440
441   for (y = 0; y < MAX_BUF_YSIZE; y++)
442   {
443     for (x = 0; x < MAX_BUF_XSIZE; x++)
444     {
445       screentiles[x][y] = -1;
446       crumbled_state[x][y] = 0;
447     }
448   }
449 }
450
451 static int getMaxCenterDistancePlayerNr(int center_x, int center_y)
452 {
453   int max_dx = 0, max_dy = 0;
454   int player_nr = game_em.last_moving_player;
455   int i;
456
457   for (i = 0; i < MAX_PLAYERS; i++)
458   {
459     if (ply[i].alive)
460     {
461       int sx = PLAYER_SCREEN_X(i);
462       int sy = PLAYER_SCREEN_Y(i);
463
464       if (game_em.last_player_direction[i] != MV_NONE &&
465           (ABS(sx - center_x) > max_dx ||
466            ABS(sy - center_y) > max_dy))
467       {
468         max_dx = MAX(max_dx, ABS(sx - center_x));
469         max_dy = MAX(max_dy, ABS(sy - center_y));
470
471         player_nr = i;
472       }
473     }
474   }
475
476   return player_nr;
477 }
478
479 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
480 {
481   boolean num_checked_players = 0;
482   int i;
483
484   for (i = 0; i < MAX_PLAYERS; i++)
485   {
486     if (ply[i].alive)
487     {
488       int sx = PLAYER_SCREEN_X(i);
489       int sy = PLAYER_SCREEN_Y(i);
490
491       if (num_checked_players == 0)
492       {
493         *sx1 = *sx2 = sx;
494         *sy1 = *sy2 = sy;
495       }
496       else
497       {
498         *sx1 = MIN(*sx1, sx);
499         *sy1 = MIN(*sy1, sy);
500         *sx2 = MAX(*sx2, sx);
501         *sy2 = MAX(*sy2, sy);
502       }
503
504       num_checked_players++;
505     }
506   }
507 }
508
509 boolean checkIfAllPlayersFitToScreen(void)
510 {
511   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
512
513   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
514
515   return (sx2 - sx1 <= SCR_FIELDX * TILEX &&
516           sy2 - sy1 <= SCR_FIELDY * TILEY);
517 }
518
519 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
520 {
521   int sx1 = screen_x, sy1 = screen_y, sx2 = screen_x, sy2 = screen_y;
522
523   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
524
525   *sx = (sx1 + sx2) / 2;
526   *sy = (sy1 + sy2) / 2;
527 }
528
529 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
530                                               int center_x, int center_y)
531 {
532   int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
533
534   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
535
536   *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
537   *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
538 }
539
540 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
541 {
542   int max_dx, max_dy;
543
544   setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
545
546   return (max_dx <= SCR_FIELDX * TILEX / 2 &&
547           max_dy <= SCR_FIELDY * TILEY / 2);
548 }
549
550 void RedrawPlayfield_EM(boolean force_redraw)
551 {
552   boolean draw_new_player_location = FALSE;
553   boolean quick_relocation = setup.quick_switch;
554   int max_center_distance_player_nr =
555     getMaxCenterDistancePlayerNr(screen_x, screen_y);
556   int stepsize = TILEX / 8;
557   int offset_raw = game.scroll_delay_value;
558   int offset_x = MIN(offset_raw, (SCR_FIELDX - 2) / 2) * TILEX;
559   int offset_y = MIN(offset_raw, (SCR_FIELDY - 2) / 2) * TILEY;
560   int screen_x_old = screen_x;
561   int screen_y_old = screen_y;
562   int x, y, sx, sy;
563   int i;
564
565   if (game.set_centered_player)
566   {
567     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen();
568
569     /* switching to "all players" only possible if all players fit to screen */
570     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
571     {
572       game.centered_player_nr_next = game.centered_player_nr;
573       game.set_centered_player = FALSE;
574     }
575
576     /* do not switch focus to non-existing (or non-active) player */
577     if (game.centered_player_nr_next >= 0 &&
578         !ply[game.centered_player_nr_next].alive)
579     {
580       game.centered_player_nr_next = game.centered_player_nr;
581       game.set_centered_player = FALSE;
582     }
583   }
584
585   /* also allow focus switching when screen is scrolled to half tile */
586   if (game.set_centered_player)
587   {
588     game.centered_player_nr = game.centered_player_nr_next;
589
590     draw_new_player_location = TRUE;
591     force_redraw = TRUE;
592
593     game.set_centered_player = FALSE;
594   }
595
596   if (game.centered_player_nr == -1)
597   {
598     if (draw_new_player_location || offset_raw == 0)
599     {
600       setScreenCenteredToAllPlayers(&sx, &sy);
601     }
602     else
603     {
604       sx = PLAYER_SCREEN_X(max_center_distance_player_nr);
605       sy = PLAYER_SCREEN_Y(max_center_distance_player_nr);
606     }
607   }
608   else
609   {
610     sx = PLAYER_SCREEN_X(game.centered_player_nr);
611     sy = PLAYER_SCREEN_Y(game.centered_player_nr);
612   }
613
614   if (draw_new_player_location && quick_relocation)
615   {
616     screen_x = VALID_SCREEN_X(sx);
617     screen_y = VALID_SCREEN_Y(sy);
618     screen_x_old = screen_x;
619     screen_y_old = screen_y;
620   }
621
622   if (draw_new_player_location && !quick_relocation)
623   {
624     unsigned int game_frame_delay_value = getGameFrameDelay_EM(20);
625     int wait_delay_value = game_frame_delay_value;
626     int screen_xx = VALID_SCREEN_X(sx);
627     int screen_yy = VALID_SCREEN_Y(sy);
628
629     while (screen_x != screen_xx || screen_y != screen_yy)
630     {
631       int dx = (screen_xx < screen_x ? +1 : screen_xx > screen_x ? -1 : 0);
632       int dy = (screen_yy < screen_y ? +1 : screen_yy > screen_y ? -1 : 0);
633       int dxx = 0, dyy = 0;
634
635       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
636         break;
637
638       if (ABS(screen_xx - screen_x) >= TILEX)
639       {
640         screen_x -= dx * TILEX;
641         dxx = dx * TILEX / 2;
642       }
643       else
644       {
645         screen_x = screen_xx;
646         dxx = 0;
647       }
648
649       if (ABS(screen_yy - screen_y) >= TILEY)
650       {
651         screen_y -= dy * TILEY;
652         dyy = dy * TILEY / 2;
653       }
654       else
655       {
656         screen_y = screen_yy;
657         dyy = 0;
658       }
659
660       /* scroll in two steps of half tile size to make things smoother */
661       screen_x += dxx;
662       screen_y += dyy;
663
664       animscreen();
665
666       for (i = 0; i < MAX_PLAYERS; i++)
667         blitplayer(&ply[i]);
668
669       BlitScreenToBitmap_EM(backbuffer);
670       BackToFront_EM();
671
672       Delay(wait_delay_value);
673
674       /* scroll second step to align at full tile size */
675       screen_x -= dxx;
676       screen_y -= dyy;
677
678       animscreen();
679
680       for (i = 0; i < MAX_PLAYERS; i++)
681         blitplayer(&ply[i]);
682
683       BlitScreenToBitmap_EM(backbuffer);
684       BackToFront_EM();
685
686       Delay(wait_delay_value);
687     }
688
689     screen_x_old = screen_x;
690     screen_y_old = screen_y;
691   }
692
693   if (force_redraw)
694   {
695     for (y = 0; y < MAX_BUF_YSIZE; y++)
696     {
697       for (x = 0; x < MAX_BUF_XSIZE; x++)
698       {
699         screentiles[x][y] = -1;
700         crumbled_state[x][y] = 0;
701       }
702     }
703   }
704
705   /* calculate new screen scrolling position, with regard to scroll delay */
706   screen_x = VALID_SCREEN_X(sx + offset_x < screen_x ? sx + offset_x :
707                             sx - offset_x > screen_x ? sx - offset_x :
708                             screen_x);
709   screen_y = VALID_SCREEN_Y(sy + offset_y < screen_y ? sy + offset_y :
710                             sy - offset_y > screen_y ? sy - offset_y :
711                             screen_y);
712
713   /* prevent scrolling further than double player step size when scrolling */
714   if (ABS(screen_x - screen_x_old) > 2 * stepsize)
715   {
716     int dx = SIGN(screen_x - screen_x_old);
717
718     screen_x = screen_x_old + dx * 2 * stepsize;
719   }
720   if (ABS(screen_y - screen_y_old) > 2 * stepsize)
721   {
722     int dy = SIGN(screen_y - screen_y_old);
723
724     screen_y = screen_y_old + dy * 2 * stepsize;
725   }
726
727   /* prevent scrolling away from the other players when focus on all players */
728   if (game.centered_player_nr == -1)
729   {
730     /* check if all players are still visible with new scrolling position */
731     if (checkIfAllPlayersAreVisible(screen_x_old, screen_y_old) &&
732         !checkIfAllPlayersAreVisible(screen_x, screen_y))
733     {
734       /* reset horizontal scroll position to last value, if needed */
735       if (!checkIfAllPlayersAreVisible(screen_x, screen_y_old))
736         screen_x = screen_x_old;
737
738       /* reset vertical scroll position to last value, if needed */
739       if (!checkIfAllPlayersAreVisible(screen_x_old, screen_y))
740         screen_y = screen_y_old;
741     }
742   }
743
744   /* prevent scrolling (for screen correcting) if no player is moving */
745   if (!game_em.any_player_moving)
746   {
747     screen_x = screen_x_old;
748     screen_y = screen_y_old;
749   }
750   else
751   {
752     /* prevent scrolling against the players move direction */
753     int player_nr = (game.centered_player_nr == -1 ?
754                      max_center_distance_player_nr : game.centered_player_nr);
755     int player_move_dir = game_em.last_player_direction[player_nr];
756     int dx = SIGN(screen_x - screen_x_old);
757     int dy = SIGN(screen_y - screen_y_old);
758
759     if ((dx < 0 && player_move_dir != MV_LEFT) ||
760         (dx > 0 && player_move_dir != MV_RIGHT))
761       screen_x = screen_x_old;
762
763     if ((dy < 0 && player_move_dir != MV_UP) ||
764         (dy > 0 && player_move_dir != MV_DOWN))
765       screen_y = screen_y_old;
766   }
767
768   // skip redrawing playfield in warp mode or when testing tapes with "autotest"
769   if (DrawingDeactivatedField())
770     return;
771
772   animscreen();
773
774   for (i = 0; i < MAX_PLAYERS; i++)
775     blitplayer(&ply[i]);
776 }