rnd-20060320-1-src
[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         1
9 #define MIN_SCREEN_YPOS         1
10 #define MAX_SCREEN_XPOS         MAX(1, lev.width  - (SCR_FIELDX - 1))
11 #define MAX_SCREEN_YPOS         MAX(1, lev.height - (SCR_FIELDY - 1))
12
13 #define MIN_SCREEN_X            (MIN_SCREEN_XPOS * TILEX)
14 #define MIN_SCREEN_Y            (MIN_SCREEN_YPOS * TILEY)
15 #define MAX_SCREEN_X            (MAX_SCREEN_XPOS * TILEX)
16 #define MAX_SCREEN_Y            (MAX_SCREEN_YPOS * TILEY)
17
18 #define VALID_SCREEN_X(x)       ((x) < MIN_SCREEN_X ? MIN_SCREEN_X :    \
19                                  (x) > MAX_SCREEN_X ? MAX_SCREEN_X : (x))
20 #define VALID_SCREEN_Y(y)       ((y) < MIN_SCREEN_Y ? MIN_SCREEN_Y :    \
21                                  (y) > MAX_SCREEN_Y ? MAX_SCREEN_Y : (y))
22
23 #define PLAYER_SCREEN_X(p)      (((    frame) * ply[p].oldx +           \
24                                   (8 - frame) * ply[p].x) * TILEX / 8   \
25                                  - ((SCR_FIELDX - 1) * TILEX) / 2)
26 #define PLAYER_SCREEN_Y(p)      (((    frame) * ply[p].oldy +           \
27                                   (8 - frame) * ply[p].y) * TILEY / 8   \
28                                  - ((SCR_FIELDY - 1) * TILEY) / 2)
29
30
31 int frame;                      /* current screen frame */
32 int screen_x;                   /* current scroll position */
33 int screen_y;
34
35 /* tiles currently on screen */
36 static int screentiles[MAX_BUF_YSIZE][MAX_BUF_XSIZE];
37 static int crumbled_state[MAX_BUF_YSIZE][MAX_BUF_XSIZE];
38
39 static boolean redraw[MAX_BUF_XSIZE][MAX_BUF_YSIZE];
40
41 #if 0
42 #if 1
43 int centered_player_nr;
44 #else
45 static int centered_player_nr;
46 #endif
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 #if 0
462   game.centered_player_nr = getCenteredPlayerNr_EM();
463 #endif
464
465   player_nr = (game.centered_player_nr != -1 ? game.centered_player_nr : 0);
466
467   screen_x = VALID_SCREEN_X(PLAYER_SCREEN_X(player_nr));
468   screen_y = VALID_SCREEN_Y(PLAYER_SCREEN_Y(player_nr));
469
470   for (y = 0; y < MAX_BUF_YSIZE; y++)
471   {
472     for (x = 0; x < MAX_BUF_XSIZE; x++)
473     {
474       screentiles[y][x] = -1;
475       crumbled_state[y][x] = 0;
476     }
477   }
478
479   DrawAllGameValues(lev.required, dynamite_state, lev.score,
480                     lev.time, all_keys_state);
481 }
482
483 #if 0
484 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
485 {
486   boolean ffwd_delay = (tape.playing && tape.fast_forward);
487   boolean no_delay = (tape.warp_forward);
488   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
489   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
490   int jx = player->jx;
491   int jy = player->jy;
492
493   if (quick_relocation)
494   {
495     int offset = (setup.scroll_delay ? 3 : 0);
496
497     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
498     {
499       scroll_x = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
500                   player->jx > SBX_Right + MIDPOSX ? SBX_Right :
501                   player->jx - MIDPOSX);
502
503       scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
504                   player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
505                   player->jy - MIDPOSY);
506     }
507     else
508     {
509       if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
510           (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
511         scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
512
513       if ((player->MovDir == MV_UP  && scroll_y > jy - MIDPOSY + offset) ||
514           (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
515         scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
516
517       /* don't scroll over playfield boundaries */
518       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
519         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
520
521       /* don't scroll over playfield boundaries */
522       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
523         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
524     }
525
526     RedrawPlayfield(TRUE, 0,0,0,0);
527   }
528   else
529   {
530     int scroll_xx = -999, scroll_yy = -999;
531
532     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
533
534     while (scroll_xx != scroll_x || scroll_yy != scroll_y)
535     {
536       int dx = 0, dy = 0;
537       int fx = FX, fy = FY;
538
539       scroll_xx = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
540                    player->jx > SBX_Right + MIDPOSX ? SBX_Right :
541                    player->jx - MIDPOSX);
542
543       scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
544                    player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
545                    player->jy - MIDPOSY);
546
547       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
548       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
549
550       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
551         break;
552
553       scroll_x -= dx;
554       scroll_y -= dy;
555
556       fx += dx * TILEX / 2;
557       fy += dy * TILEY / 2;
558
559       ScrollLevel(dx, dy);
560       DrawAllPlayers();
561
562       /* scroll in two steps of half tile size to make things smoother */
563       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
564       FlushDisplay();
565       Delay(wait_delay_value);
566
567       /* scroll second step to align at full tile size */
568       BackToFront();
569       Delay(wait_delay_value);
570     }
571
572     DrawPlayer(player);
573     BackToFront();
574     Delay(wait_delay_value);
575   }
576 }
577 #endif
578
579 static int getMaxCenterDistancePlayerNr(int center_x, int center_y)
580 {
581   int max_dx = 0, max_dy = 0;
582   int player_nr = game_em.last_moving_player;
583   int i;
584
585   for (i = 0; i < MAX_PLAYERS; i++)
586   {
587     if (ply[i].alive)
588     {
589       int sx = PLAYER_SCREEN_X(i);
590       int sy = PLAYER_SCREEN_Y(i);
591
592       if (game_em.last_player_direction[i] != MV_NONE &&
593           (ABS(sx - center_x) > max_dx ||
594            ABS(sy - center_y) > max_dy))
595       {
596         max_dx = MAX(max_dx, ABS(sx - center_x));
597         max_dy = MAX(max_dy, ABS(sy - center_y));
598
599         player_nr = i;
600       }
601     }
602   }
603
604   return player_nr;
605 }
606
607 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
608 {
609   boolean num_checked_players = 0;
610   int i;
611
612   for (i = 0; i < MAX_PLAYERS; i++)
613   {
614     if (ply[i].alive)
615     {
616       int sx = PLAYER_SCREEN_X(i);
617       int sy = PLAYER_SCREEN_Y(i);
618
619       if (num_checked_players == 0)
620       {
621         *sx1 = *sx2 = sx;
622         *sy1 = *sy2 = sy;
623       }
624       else
625       {
626         *sx1 = MIN(*sx1, sx);
627         *sy1 = MIN(*sy1, sy);
628         *sx2 = MAX(*sx2, sx);
629         *sy2 = MAX(*sy2, sy);
630       }
631
632       num_checked_players++;
633     }
634   }
635 }
636
637 boolean checkIfAllPlayersFitToScreen()
638 {
639   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
640
641   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
642
643   return (sx2 - sx1 <= SCR_FIELDX * TILEX &&
644           sy2 - sy1 <= SCR_FIELDY * TILEY);
645 }
646
647 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
648 {
649   int sx1 = screen_x, sy1 = screen_y, sx2 = screen_x, sy2 = screen_y;
650
651   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
652
653   *sx = (sx1 + sx2) / 2;
654   *sy = (sy1 + sy2) / 2;
655 }
656
657 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
658                                               int center_x, int center_y)
659 {
660   int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
661
662   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
663
664   *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
665   *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
666 }
667
668 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
669 {
670   int max_dx, max_dy;
671
672   setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
673
674   return (max_dx <= SCR_FIELDX * TILEX / 2 &&
675           max_dy <= SCR_FIELDY * TILEY / 2);
676 }
677
678 void RedrawPlayfield_EM(boolean force_redraw)
679 {
680 #if 0
681   boolean all_players_visible = checkIfAllPlayersAreVisible();
682 #endif
683   boolean draw_new_player_location = FALSE;
684   boolean quick_relocation = setup.quick_switch;
685 #if 0
686   boolean scrolling = (screen_x % TILEX != 0 || screen_y % TILEY != 0);
687 #endif
688 #if 0
689   boolean game.set_centered_player = getSetCenteredPlayer_EM();
690   int game.centered_player_nr_next = getCenteredPlayerNr_EM();
691 #endif
692 #if 1
693   int player_nr = getMaxCenterDistancePlayerNr(screen_x, screen_y);
694 #else
695   int player_nr = game_em.last_moving_player;
696 #endif
697   int stepsize = TILEX / 8;
698   int offset = (setup.scroll_delay ? 3 : 0) * TILEX;
699   int offset_x = offset;
700   int offset_y = offset;
701   int screen_x_old = screen_x;
702   int screen_y_old = screen_y;
703   int x, y, sx, sy;
704   int i;
705
706   if (game.set_centered_player)
707   {
708     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen();
709
710     /* switching to "all players" only possible if all players fit to screen */
711     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
712     {
713       game.centered_player_nr_next = game.centered_player_nr;
714       game.set_centered_player = FALSE;
715     }
716
717     /* do not switch focus to non-existing (or non-active) player */
718     if (game.centered_player_nr_next >= 0 &&
719         !ply[game.centered_player_nr_next].alive)
720     {
721       game.centered_player_nr_next = game.centered_player_nr;
722       game.set_centered_player = FALSE;
723     }
724   }
725
726 #if 1
727   /* also allow focus switching when screen is scrolled to half tile */
728 #else
729   if (!scrolling)       /* screen currently aligned at tile position */
730 #endif
731   {
732 #if 1
733     if (game.set_centered_player)
734 #else
735     if (game.centered_player_nr != game.centered_player_nr_next)
736 #endif
737     {
738       game.centered_player_nr = game.centered_player_nr_next;
739
740       draw_new_player_location = TRUE;
741       force_redraw = TRUE;
742
743       game.set_centered_player = FALSE;
744     }
745   }
746
747   if (game.centered_player_nr == -1)
748   {
749 #if 1
750     if (draw_new_player_location || offset == 0)
751 #else
752     if (draw_new_player_location)
753 #endif
754     {
755       setScreenCenteredToAllPlayers(&sx, &sy);
756     }
757     else
758     {
759 #if 1
760       sx = PLAYER_SCREEN_X(player_nr);
761       sy = PLAYER_SCREEN_Y(player_nr);
762 #else
763       sx = PLAYER_SCREEN_X(game_em.last_moving_player);
764       sy = PLAYER_SCREEN_Y(game_em.last_moving_player);
765 #endif
766     }
767   }
768   else
769   {
770     sx = PLAYER_SCREEN_X(game.centered_player_nr);
771     sy = PLAYER_SCREEN_Y(game.centered_player_nr);
772   }
773
774   if (draw_new_player_location && quick_relocation)
775   {
776     screen_x = VALID_SCREEN_X(sx);
777     screen_y = VALID_SCREEN_Y(sy);
778     screen_x_old = screen_x;
779     screen_y_old = screen_y;
780
781 #if 0
782     offset_x = 0;
783     offset_y = 0;
784 #endif
785   }
786
787   if (draw_new_player_location && !quick_relocation)
788   {
789 #if 1
790     unsigned long game_frame_delay_value = getGameFrameDelay_EM(20);
791 #else
792     unsigned long game_frame_delay_value = getGameFrameDelay_EM(25);
793 #endif
794     int wait_delay_value = game_frame_delay_value;
795     int screen_xx = VALID_SCREEN_X(sx);
796     int screen_yy = VALID_SCREEN_Y(sy);
797
798     while (screen_x != screen_xx || screen_y != screen_yy)
799     {
800       int dx = (screen_xx < screen_x ? +1 : screen_xx > screen_x ? -1 : 0);
801       int dy = (screen_yy < screen_y ? +1 : screen_yy > screen_y ? -1 : 0);
802       int dxx = 0, dyy = 0;
803
804       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
805         break;
806
807 #if 1
808
809       if (ABS(screen_xx - screen_x) >= TILEX)
810       {
811         screen_x -= dx * TILEX;
812         dxx = dx * TILEX / 2;
813       }
814       else
815       {
816         screen_x = screen_xx;
817         dxx = 0;
818       }
819
820       if (ABS(screen_yy - screen_y) >= TILEY)
821       {
822         screen_y -= dy * TILEY;
823         dyy = dy * TILEY / 2;
824       }
825       else
826       {
827         screen_y = screen_yy;
828         dyy = 0;
829       }
830
831 #else
832
833 #if 1
834       if (ABS(screen_xx - screen_x) >= TILEX ||
835           ABS(screen_yy - screen_y) >= TILEY)
836       {
837         screen_x -= dx * TILEX;
838         screen_y -= dy * TILEY;
839
840         dxx = dx * TILEX / 2;
841         dyy = dy * TILEY / 2;
842       }
843       else
844       {
845         screen_x = screen_xx;
846         screen_y = screen_yy;
847
848         dxx = 0;
849         dyy = 0;
850       }
851 #else
852       screen_x -= dx * TILEX;
853       screen_y -= dy * TILEY;
854
855       dxx += dx * TILEX / 2;
856       dyy += dy * TILEY / 2;
857 #endif
858
859 #endif
860
861       /* scroll in two steps of half tile size to make things smoother */
862       screen_x += dxx;
863       screen_y += dyy;
864
865       animscreen();
866
867       for (i = 0; i < MAX_PLAYERS; i++)
868         blitplayer(&ply[i]);
869
870       blitscreen();
871       FlushDisplay();
872       Delay(wait_delay_value);
873
874       /* scroll second step to align at full tile size */
875       screen_x -= dxx;
876       screen_y -= dyy;
877
878       SyncDisplay();
879
880       animscreen();
881
882       for (i = 0; i < MAX_PLAYERS; i++)
883         blitplayer(&ply[i]);
884
885       blitscreen();
886       FlushDisplay();
887       Delay(wait_delay_value);
888     }
889
890     screen_x_old = screen_x;
891     screen_y_old = screen_y;
892   }
893
894   if (force_redraw)
895   {
896     for (y = 0; y < MAX_BUF_YSIZE; y++)
897     {
898       for (x = 0; x < MAX_BUF_XSIZE; x++)
899       {
900         screentiles[y][x] = -1;
901         crumbled_state[y][x] = 0;
902       }
903     }
904   }
905
906   /* calculate new screen scrolling position, with regard to scroll delay */
907   screen_x = VALID_SCREEN_X(sx + offset_x < screen_x ? sx + offset_x :
908                             sx - offset_x > screen_x ? sx - offset_x :
909                             screen_x);
910   screen_y = VALID_SCREEN_Y(sy + offset_y < screen_y ? sy + offset_y :
911                             sy - offset_y > screen_y ? sy - offset_y :
912                             screen_y);
913
914 #if 1
915   /* prevent scrolling further than double player step size when scrolling */
916   if (ABS(screen_x - screen_x_old) > 2 * stepsize ||
917       ABS(screen_y - screen_y_old) > 2 * stepsize)
918   {
919     int dx = SIGN(screen_x - screen_x_old);
920     int dy = SIGN(screen_y - screen_y_old);
921
922     screen_x = screen_x_old + dx * 2 * stepsize;
923     screen_y = screen_y_old + dy * 2 * stepsize;
924   }
925 #else
926   /* prevent scrolling further than player step size when scrolling */
927   if (ABS(screen_x - screen_x_old) > stepsize ||
928       ABS(screen_y - screen_y_old) > stepsize)
929   {
930     int dx = SIGN(screen_x - screen_x_old);
931     int dy = SIGN(screen_y - screen_y_old);
932
933     screen_x = screen_x_old + dx * stepsize;
934     screen_y = screen_y_old + dy * stepsize;
935   }
936 #endif
937
938   /* prevent scrolling away from the other players when focus on all players */
939   if (game.centered_player_nr == -1)
940   {
941 #if 1
942     /* check if all players are still visible with new scrolling position */
943     if (checkIfAllPlayersAreVisible(screen_x_old, screen_y_old) &&
944         !checkIfAllPlayersAreVisible(screen_x, screen_y))
945     {
946       /* reset horizontal scroll position to last value, if needed */
947       if (!checkIfAllPlayersAreVisible(screen_x, screen_y_old))
948         screen_x = screen_x_old;
949
950       /* reset vertical scroll position to last value, if needed */
951       if (!checkIfAllPlayersAreVisible(screen_x_old, screen_y))
952         screen_y = screen_y_old;
953     }
954 #else
955     boolean all_players_visible = checkIfAllPlayersAreVisible();
956
957     if (!all_players_visible)
958     {
959       printf("::: not all players visible\n");
960
961       screen_x = screen_x_old;
962       screen_y = screen_y_old;
963     }
964 #endif
965   }
966
967   /* prevent scrolling (for screen correcting) if no player is moving */
968   if (!game_em.any_player_moving)
969   {
970     screen_x = screen_x_old;
971     screen_y = screen_y_old;
972   }
973   else
974   {
975     /* prevent scrolling against the players move direction */
976 #if 0
977     int player_nr = game_em.last_moving_player;
978 #endif
979     int player_move_dir = game_em.last_player_direction[player_nr];
980     int dx = SIGN(screen_x - screen_x_old);
981     int dy = SIGN(screen_y - screen_y_old);
982
983     if ((dx < 0 && player_move_dir != MV_LEFT) ||
984         (dx > 0 && player_move_dir != MV_RIGHT))
985       screen_x = screen_x_old;
986
987     if ((dy < 0 && player_move_dir != MV_UP) ||
988         (dy > 0 && player_move_dir != MV_DOWN))
989       screen_y = screen_y_old;
990   }
991
992   animscreen();
993
994   for (i = 0; i < MAX_PLAYERS; i++)
995     blitplayer(&ply[i]);
996
997   SyncDisplay();
998
999   blitscreen();
1000
1001   FlushDisplay();
1002 }
1003
1004 void game_animscreen(void)
1005 {
1006   RedrawPlayfield_EM(FALSE);
1007 }
1008
1009 void DrawGameDoorValues_EM()
1010 {
1011   int dynamite_state = ply[0].dynamite;         /* !!! ONLY PLAYER 1 !!! */
1012   int all_keys_state = ply[0].keys | ply[1].keys | ply[2].keys | ply[3].keys;
1013
1014 #if 1
1015   DrawAllGameValues(lev.required, dynamite_state, lev.score,
1016                     lev.time, all_keys_state);
1017 #else
1018   DrawAllGameValues(lev.required, ply1.dynamite, lev.score,
1019                     DISPLAY_TIME(lev.time), ply1.keys | ply2.keys);
1020 #endif
1021 }