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