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