rnd-20060319-2-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 offset = (setup.scroll_delay ? 3 : 0) * TILEX;
698   int offset_x = offset;
699   int offset_y = offset;
700   int screen_x_old = screen_x;
701   int screen_y_old = screen_y;
702   int x, y, sx, sy;
703   int i;
704
705   if (game.set_centered_player)
706   {
707     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen();
708
709     /* switching to "all players" only possible if all players fit to screen */
710     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
711     {
712       game.centered_player_nr_next = game.centered_player_nr;
713       game.set_centered_player = FALSE;
714     }
715
716     /* do not switch focus to non-existing (or non-active) player */
717     if (game.centered_player_nr_next >= 0 &&
718         !ply[game.centered_player_nr_next].alive)
719     {
720       game.centered_player_nr_next = game.centered_player_nr;
721       game.set_centered_player = FALSE;
722     }
723   }
724
725 #if 1
726   /* also allow focus switching when screen is scrolled to half tile */
727 #else
728   if (!scrolling)       /* screen currently aligned at tile position */
729 #endif
730   {
731 #if 1
732     if (game.set_centered_player)
733 #else
734     if (game.centered_player_nr != game.centered_player_nr_next)
735 #endif
736     {
737       game.centered_player_nr = game.centered_player_nr_next;
738
739       draw_new_player_location = TRUE;
740       force_redraw = TRUE;
741
742       game.set_centered_player = FALSE;
743     }
744   }
745
746   if (game.centered_player_nr == -1)
747   {
748     if (draw_new_player_location)
749     {
750       setScreenCenteredToAllPlayers(&sx, &sy);
751     }
752     else
753     {
754 #if 1
755       sx = PLAYER_SCREEN_X(player_nr);
756       sy = PLAYER_SCREEN_Y(player_nr);
757 #else
758       sx = PLAYER_SCREEN_X(game_em.last_moving_player);
759       sy = PLAYER_SCREEN_Y(game_em.last_moving_player);
760 #endif
761     }
762   }
763   else
764   {
765     sx = PLAYER_SCREEN_X(game.centered_player_nr);
766     sy = PLAYER_SCREEN_Y(game.centered_player_nr);
767   }
768
769   if (draw_new_player_location && quick_relocation)
770   {
771     screen_x = VALID_SCREEN_X(sx);
772     screen_y = VALID_SCREEN_Y(sy);
773     screen_x_old = screen_x;
774     screen_y_old = screen_y;
775
776 #if 0
777     offset_x = 0;
778     offset_y = 0;
779 #endif
780   }
781
782   if (draw_new_player_location && !quick_relocation)
783   {
784 #if 1
785     unsigned long game_frame_delay_value = getGameFrameDelay_EM(20);
786 #else
787     unsigned long game_frame_delay_value = getGameFrameDelay_EM(25);
788 #endif
789     int wait_delay_value = game_frame_delay_value;
790     int screen_xx = VALID_SCREEN_X(sx);
791     int screen_yy = VALID_SCREEN_Y(sy);
792
793     while (screen_x != screen_xx || screen_y != screen_yy)
794     {
795       int dx = (screen_xx < screen_x ? +1 : screen_xx > screen_x ? -1 : 0);
796       int dy = (screen_yy < screen_y ? +1 : screen_yy > screen_y ? -1 : 0);
797       int dxx = 0, dyy = 0;
798
799       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
800         break;
801
802 #if 1
803
804       if (ABS(screen_xx - screen_x) >= TILEX)
805       {
806         screen_x -= dx * TILEX;
807         dxx = dx * TILEX / 2;
808       }
809       else
810       {
811         screen_x = screen_xx;
812         dxx = 0;
813       }
814
815       if (ABS(screen_yy - screen_y) >= TILEY)
816       {
817         screen_y -= dy * TILEY;
818         dyy = dy * TILEY / 2;
819       }
820       else
821       {
822         screen_y = screen_yy;
823         dyy = 0;
824       }
825
826 #else
827
828 #if 1
829       if (ABS(screen_xx - screen_x) >= TILEX ||
830           ABS(screen_yy - screen_y) >= TILEY)
831       {
832         screen_x -= dx * TILEX;
833         screen_y -= dy * TILEY;
834
835         dxx = dx * TILEX / 2;
836         dyy = dy * TILEY / 2;
837       }
838       else
839       {
840         screen_x = screen_xx;
841         screen_y = screen_yy;
842
843         dxx = 0;
844         dyy = 0;
845       }
846 #else
847       screen_x -= dx * TILEX;
848       screen_y -= dy * TILEY;
849
850       dxx += dx * TILEX / 2;
851       dyy += dy * TILEY / 2;
852 #endif
853
854 #endif
855
856       /* scroll in two steps of half tile size to make things smoother */
857       screen_x += dxx;
858       screen_y += dyy;
859
860       animscreen();
861
862       for (i = 0; i < MAX_PLAYERS; i++)
863         blitplayer(&ply[i]);
864
865       blitscreen();
866       FlushDisplay();
867       Delay(wait_delay_value);
868
869       /* scroll second step to align at full tile size */
870       screen_x -= dxx;
871       screen_y -= dyy;
872
873       SyncDisplay();
874
875       animscreen();
876
877       for (i = 0; i < MAX_PLAYERS; i++)
878         blitplayer(&ply[i]);
879
880       blitscreen();
881       FlushDisplay();
882       Delay(wait_delay_value);
883     }
884
885     screen_x_old = screen_x;
886     screen_y_old = screen_y;
887   }
888
889   if (force_redraw)
890   {
891     for (y = 0; y < MAX_BUF_YSIZE; y++)
892     {
893       for (x = 0; x < MAX_BUF_XSIZE; x++)
894       {
895         screentiles[y][x] = -1;
896         crumbled_state[y][x] = 0;
897       }
898     }
899   }
900
901   /* calculate new screen scrolling position, with regard to scroll delay */
902   screen_x = VALID_SCREEN_X(sx + offset_x < screen_x ? sx + offset_x :
903                             sx - offset_x > screen_x ? sx - offset_x :
904                             screen_x);
905   screen_y = VALID_SCREEN_Y(sy + offset_y < screen_y ? sy + offset_y :
906                             sy - offset_y > screen_y ? sy - offset_y :
907                             screen_y);
908
909   /* prevent scrolling further than player step size screen when scrolling */
910   if (ABS(screen_x - screen_x_old) > TILEX / 8 ||
911       ABS(screen_y - screen_y_old) > TILEY / 8)
912   {
913     int dx = SIGN(screen_x - screen_x_old);
914     int dy = SIGN(screen_y - screen_y_old);
915
916     screen_x = screen_x_old + dx * TILEX / 8;
917     screen_y = screen_y_old + dy * TILEY / 8;
918   }
919
920   /* prevent scrolling away from the other players when focus on all players */
921   if (game.centered_player_nr == -1)
922   {
923 #if 1
924     /* check if all players are still visible with new scrolling position */
925     if (!checkIfAllPlayersAreVisible(screen_x, screen_y))
926     {
927       /* reset horizontal scroll position to last value, if needed */
928       if (!checkIfAllPlayersAreVisible(screen_x, screen_y_old))
929         screen_x = screen_x_old;
930
931       /* reset vertical scroll position to last value, if needed */
932       if (!checkIfAllPlayersAreVisible(screen_x_old, screen_y))
933         screen_y = screen_y_old;
934     }
935 #else
936     boolean all_players_visible = checkIfAllPlayersAreVisible();
937
938     if (!all_players_visible)
939     {
940       printf("::: not all players visible\n");
941
942       screen_x = screen_x_old;
943       screen_y = screen_y_old;
944     }
945 #endif
946   }
947
948   /* prevent scrolling (for screen correcting) if no player is moving */
949   if (!game_em.any_player_moving)
950   {
951     screen_x = screen_x_old;
952     screen_y = screen_y_old;
953   }
954   else
955   {
956     /* prevent scrolling against the players move direction */
957 #if 0
958     int player_nr = game_em.last_moving_player;
959 #endif
960     int player_move_dir = game_em.last_player_direction[player_nr];
961     int dx = SIGN(screen_x - screen_x_old);
962     int dy = SIGN(screen_y - screen_y_old);
963
964     if ((dx < 0 && player_move_dir != MV_LEFT) ||
965         (dx > 0 && player_move_dir != MV_RIGHT))
966       screen_x = screen_x_old;
967
968     if ((dy < 0 && player_move_dir != MV_UP) ||
969         (dy > 0 && player_move_dir != MV_DOWN))
970       screen_y = screen_y_old;
971   }
972
973   animscreen();
974
975   for (i = 0; i < MAX_PLAYERS; i++)
976     blitplayer(&ply[i]);
977
978   SyncDisplay();
979
980   blitscreen();
981
982   FlushDisplay();
983 }
984
985 void game_animscreen(void)
986 {
987   RedrawPlayfield_EM(FALSE);
988 }
989
990 void DrawGameDoorValues_EM()
991 {
992   int dynamite_state = ply[0].dynamite;         /* !!! ONLY PLAYER 1 !!! */
993   int all_keys_state = ply[0].keys | ply[1].keys | ply[2].keys | ply[3].keys;
994
995 #if 1
996   DrawAllGameValues(lev.required, dynamite_state, lev.score,
997                     lev.time, all_keys_state);
998 #else
999   DrawAllGameValues(lev.required, ply1.dynamite, lev.score,
1000                     DISPLAY_TIME(lev.time), ply1.keys | ply2.keys);
1001 #endif
1002 }