fixed graphical bug with wrong draw buffer when using envelope request
[rocksndiamonds.git] / src / tools.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // tools.c
10 // ============================================================================
11
12 #include <math.h>
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "init.h"
18 #include "game.h"
19 #include "events.h"
20 #include "anim.h"
21 #include "network.h"
22 #include "tape.h"
23 #include "screens.h"
24
25
26 #define DEBUG_FRAME_TIME        FALSE
27
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES        0
30 #define TOOL_CTRL_ID_NO         1
31 #define TOOL_CTRL_ID_CONFIRM    2
32 #define TOOL_CTRL_ID_PLAYER_1   3
33 #define TOOL_CTRL_ID_PLAYER_2   4
34 #define TOOL_CTRL_ID_PLAYER_3   5
35 #define TOOL_CTRL_ID_PLAYER_4   6
36 #define TOOL_CTRL_ID_TOUCH_YES  7
37 #define TOOL_CTRL_ID_TOUCH_NO   8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
39
40 #define NUM_TOOL_BUTTONS        10
41
42 // constants for number of doors and door parts
43 #define NUM_DOORS               2
44 #define NUM_PANELS              NUM_DOORS
45 // #define NUM_PANELS           0
46 #define MAX_PARTS_PER_DOOR      8
47 #define MAX_DOOR_PARTS          (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i)   ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49
50
51 struct DoorPartOrderInfo
52 {
53   int nr;
54   int sort_priority;
55 };
56
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58
59 struct DoorPartControlInfo
60 {
61   int door_token;
62   int graphic;
63   struct DoorPartPosInfo *pos;
64 };
65
66 static struct DoorPartControlInfo door_part_controls[] =
67 {
68   {
69     DOOR_1,
70     IMG_GFX_DOOR_1_PART_1,
71     &door_1.part_1
72   },
73   {
74     DOOR_1,
75     IMG_GFX_DOOR_1_PART_2,
76     &door_1.part_2
77   },
78   {
79     DOOR_1,
80     IMG_GFX_DOOR_1_PART_3,
81     &door_1.part_3
82   },
83   {
84     DOOR_1,
85     IMG_GFX_DOOR_1_PART_4,
86     &door_1.part_4
87   },
88   {
89     DOOR_1,
90     IMG_GFX_DOOR_1_PART_5,
91     &door_1.part_5
92   },
93   {
94     DOOR_1,
95     IMG_GFX_DOOR_1_PART_6,
96     &door_1.part_6
97   },
98   {
99     DOOR_1,
100     IMG_GFX_DOOR_1_PART_7,
101     &door_1.part_7
102   },
103   {
104     DOOR_1,
105     IMG_GFX_DOOR_1_PART_8,
106     &door_1.part_8
107   },
108
109   {
110     DOOR_2,
111     IMG_GFX_DOOR_2_PART_1,
112     &door_2.part_1
113   },
114   {
115     DOOR_2,
116     IMG_GFX_DOOR_2_PART_2,
117     &door_2.part_2
118   },
119   {
120     DOOR_2,
121     IMG_GFX_DOOR_2_PART_3,
122     &door_2.part_3
123   },
124   {
125     DOOR_2,
126     IMG_GFX_DOOR_2_PART_4,
127     &door_2.part_4
128   },
129   {
130     DOOR_2,
131     IMG_GFX_DOOR_2_PART_5,
132     &door_2.part_5
133   },
134   {
135     DOOR_2,
136     IMG_GFX_DOOR_2_PART_6,
137     &door_2.part_6
138   },
139   {
140     DOOR_2,
141     IMG_GFX_DOOR_2_PART_7,
142     &door_2.part_7
143   },
144   {
145     DOOR_2,
146     IMG_GFX_DOOR_2_PART_8,
147     &door_2.part_8
148   },
149
150   {
151     DOOR_1,
152     IMG_BACKGROUND_PANEL,
153     &door_1.panel
154   },
155   {
156     DOOR_2,
157     IMG_BACKGROUND_TAPE,
158     &door_2.panel
159   },
160
161   {
162     -1,
163     -1,
164     NULL
165   }
166 };
167
168
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
174
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
177
178 static char *print_if_not_empty(int element)
179 {
180   static char *s = NULL;
181   char *token_name = element_info[element].token_name;
182
183   if (s != NULL)
184     free(s);
185
186   s = checked_malloc(strlen(token_name) + 10 + 1);
187
188   if (element != EL_EMPTY)
189     sprintf(s, "%d\t['%s']", element, token_name);
190   else
191     sprintf(s, "%d", element);
192
193   return s;
194 }
195
196 int getFieldbufferOffsetX_RND(int dir, int pos)
197 {
198   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199   int dx = (dir & MV_HORIZONTAL ? pos : 0);
200   int dx_var = dx * TILESIZE_VAR / TILESIZE;
201   int fx = FX;
202
203   if (EVEN(SCR_FIELDX))
204   {
205     int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206     int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
207
208     if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209       fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
210     else
211       fx += (dx_var > 0 ? TILEX_VAR : 0);
212   }
213   else
214   {
215     fx += dx_var;
216   }
217
218   if (full_lev_fieldx <= SCR_FIELDX)
219   {
220     if (EVEN(SCR_FIELDX))
221       fx = 2 * TILEX_VAR - (ODD(lev_fieldx)  ? TILEX_VAR / 2 : 0);
222     else
223       fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
224   }
225
226   return fx;
227 }
228
229 int getFieldbufferOffsetY_RND(int dir, int pos)
230 {
231   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232   int dy = (dir & MV_VERTICAL ? pos : 0);
233   int dy_var = dy * TILESIZE_VAR / TILESIZE;
234   int fy = FY;
235
236   if (EVEN(SCR_FIELDY))
237   {
238     int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239     int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
240
241     if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242       fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
243     else
244       fy += (dy_var > 0 ? TILEY_VAR : 0);
245   }
246   else
247   {
248     fy += dy_var;
249   }
250
251   if (full_lev_fieldy <= SCR_FIELDY)
252   {
253     if (EVEN(SCR_FIELDY))
254       fy = 2 * TILEY_VAR - (ODD(lev_fieldy)  ? TILEY_VAR / 2 : 0);
255     else
256       fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
257   }
258
259   return fy;
260 }
261
262 static int getLevelFromScreenX_RND(int sx)
263 {
264   int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
265   int dx = fx - FX;
266   int px = sx - SX;
267   int lx = LEVELX((px + dx) / TILESIZE_VAR);
268
269   return lx;
270 }
271
272 static int getLevelFromScreenY_RND(int sy)
273 {
274   int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
275   int dy = fy - FY;
276   int py = sy - SY;
277   int ly = LEVELY((py + dy) / TILESIZE_VAR);
278
279   return ly;
280 }
281
282 static int getLevelFromScreenX_EM(int sx)
283 {
284   int level_xsize = level.native_em_level->cav->width;
285   int full_xsize = level_xsize * TILESIZE_VAR;
286
287   sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
288
289   int fx = getFieldbufferOffsetX_EM();
290   int dx = fx;
291   int px = sx - SX;
292   int lx = LEVELX((px + dx) / TILESIZE_VAR);
293
294   return lx;
295 }
296
297 static int getLevelFromScreenY_EM(int sy)
298 {
299   int level_ysize = level.native_em_level->cav->height;
300   int full_ysize = level_ysize * TILESIZE_VAR;
301
302   sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
303
304   int fy = getFieldbufferOffsetY_EM();
305   int dy = fy;
306   int py = sy - SY;
307   int ly = LEVELY((py + dy) / TILESIZE_VAR);
308
309   return ly;
310 }
311
312 static int getLevelFromScreenX_SP(int sx)
313 {
314   int menBorder = setup.sp_show_border_elements;
315   int level_xsize = level.native_sp_level->width;
316   int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
317
318   sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
319
320   int fx = getFieldbufferOffsetX_SP();
321   int dx = fx - FX;
322   int px = sx - SX;
323   int lx = LEVELX((px + dx) / TILESIZE_VAR);
324
325   return lx;
326 }
327
328 static int getLevelFromScreenY_SP(int sy)
329 {
330   int menBorder = setup.sp_show_border_elements;
331   int level_ysize = level.native_sp_level->height;
332   int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
333
334   sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
335
336   int fy = getFieldbufferOffsetY_SP();
337   int dy = fy - FY;
338   int py = sy - SY;
339   int ly = LEVELY((py + dy) / TILESIZE_VAR);
340
341   return ly;
342 }
343
344 static int getLevelFromScreenX_MM(int sx)
345 {
346   int level_xsize = level.native_mm_level->fieldx;
347   int full_xsize = level_xsize * TILESIZE_VAR;
348
349   sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
350
351   int px = sx - SX;
352   int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
353
354   return lx;
355 }
356
357 static int getLevelFromScreenY_MM(int sy)
358 {
359   int level_ysize = level.native_mm_level->fieldy;
360   int full_ysize = level_ysize * TILESIZE_VAR;
361
362   sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
363
364   int py = sy - SY;
365   int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
366
367   return ly;
368 }
369
370 int getLevelFromScreenX(int x)
371 {
372   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373     return getLevelFromScreenX_EM(x);
374   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375     return getLevelFromScreenX_SP(x);
376   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377     return getLevelFromScreenX_MM(x);
378   else
379     return getLevelFromScreenX_RND(x);
380 }
381
382 int getLevelFromScreenY(int y)
383 {
384   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385     return getLevelFromScreenY_EM(y);
386   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387     return getLevelFromScreenY_SP(y);
388   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389     return getLevelFromScreenY_MM(y);
390   else
391     return getLevelFromScreenY_RND(y);
392 }
393
394 int getScreenFieldSizeX(void)
395 {
396   return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
397 }
398
399 int getScreenFieldSizeY(void)
400 {
401   return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
402 }
403
404 void DumpTile(int x, int y)
405 {
406   int sx = SCREENX(x);
407   int sy = SCREENY(y);
408   char *token_name;
409
410   Info("---");
411   Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
412   Info("---");
413
414   if (!IN_LEV_FIELD(x, y))
415   {
416     Info("(not in level field)");
417     Info("");
418
419     return;
420   }
421
422   token_name = element_info[Tile[x][y]].token_name;
423
424   Info("Tile:        %d\t['%s']",       Tile[x][y], token_name);
425   Info("Back:        %s",               print_if_not_empty(Back[x][y]));
426   Info("Store:       %s",               print_if_not_empty(Store[x][y]));
427   Info("Store2:      %s",               print_if_not_empty(Store2[x][y]));
428   Info("StorePlayer: %s",               print_if_not_empty(StorePlayer[x][y]));
429   Info("MovPos:      %d",               MovPos[x][y]);
430   Info("MovDir:      %d",               MovDir[x][y]);
431   Info("MovDelay:    %d",               MovDelay[x][y]);
432   Info("ChangeDelay: %d",               ChangeDelay[x][y]);
433   Info("CustomValue: %d",               CustomValue[x][y]);
434   Info("GfxElement:  %d",               GfxElement[x][y]);
435   Info("GfxAction:   %d",               GfxAction[x][y]);
436   Info("GfxFrame:    %d [%d]",          GfxFrame[x][y], FrameCounter);
437   Info("Player x/y:  %d, %d",           local_player->jx, local_player->jy);
438   Info("");
439 }
440
441 void DumpTileFromScreen(int sx, int sy)
442 {
443   int lx = getLevelFromScreenX(sx);
444   int ly = getLevelFromScreenY(sy);
445
446   DumpTile(lx, ly);
447 }
448
449 void SetDrawtoField(int mode)
450 {
451   if (mode == DRAW_TO_FIELDBUFFER)
452   {
453     FX = 2 * TILEX_VAR;
454     FY = 2 * TILEY_VAR;
455     BX1 = -2;
456     BY1 = -2;
457     BX2 = SCR_FIELDX + 1;
458     BY2 = SCR_FIELDY + 1;
459
460     drawto_field = fieldbuffer;
461   }
462   else  // DRAW_TO_BACKBUFFER
463   {
464     FX = SX;
465     FY = SY;
466     BX1 = 0;
467     BY1 = 0;
468     BX2 = SCR_FIELDX - 1;
469     BY2 = SCR_FIELDY - 1;
470
471     drawto_field = backbuffer;
472   }
473 }
474
475 int GetDrawtoField(void)
476 {
477   return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
478 }
479
480 static void RedrawPlayfield_RND(void)
481 {
482   if (game.envelope_active)
483     return;
484
485   DrawLevel(REDRAW_ALL);
486   DrawAllPlayers();
487 }
488
489 void RedrawPlayfield(void)
490 {
491   if (game_status != GAME_MODE_PLAYING)
492     return;
493
494   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495     RedrawPlayfield_EM(TRUE);
496   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497     RedrawPlayfield_SP(TRUE);
498   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499     RedrawPlayfield_MM();
500   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501     RedrawPlayfield_RND();
502
503   BlitScreenToBitmap(backbuffer);
504
505   BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
506              gfx.sx, gfx.sy);
507 }
508
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
510                                      int draw_target)
511 {
512   Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513   Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
514
515   if (x == -1 && y == -1)
516     return;
517
518   if (draw_target == DRAW_TO_SCREEN)
519     BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
520   else
521     BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
522 }
523
524 static void DrawMaskedBorderExt_FIELD(int draw_target)
525 {
526   if (global.border_status >= GAME_MODE_MAIN &&
527       global.border_status <= GAME_MODE_PLAYING &&
528       border.draw_masked[global.border_status])
529     DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
530                              draw_target);
531 }
532
533 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
534 {
535   // when drawing to backbuffer, never draw border over open doors
536   if (draw_target == DRAW_TO_BACKBUFFER &&
537       (GetDoorState() & DOOR_OPEN_1))
538     return;
539
540   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
541       (global.border_status != GAME_MODE_EDITOR ||
542        border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
543     DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
544 }
545
546 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
547 {
548   // when drawing to backbuffer, never draw border over open doors
549   if (draw_target == DRAW_TO_BACKBUFFER &&
550       (GetDoorState() & DOOR_OPEN_2))
551     return;
552
553   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554       global.border_status != GAME_MODE_EDITOR)
555     DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
556 }
557
558 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
559 {
560   // currently not available
561 }
562
563 static void DrawMaskedBorderExt_ALL(int draw_target)
564 {
565   DrawMaskedBorderExt_FIELD(draw_target);
566   DrawMaskedBorderExt_DOOR_1(draw_target);
567   DrawMaskedBorderExt_DOOR_2(draw_target);
568   DrawMaskedBorderExt_DOOR_3(draw_target);
569 }
570
571 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
572 {
573   // never draw masked screen borders on borderless screens
574   if (global.border_status == GAME_MODE_LOADING ||
575       global.border_status == GAME_MODE_TITLE)
576     return;
577
578   if (redraw_mask & REDRAW_ALL)
579     DrawMaskedBorderExt_ALL(draw_target);
580   else
581   {
582     if (redraw_mask & REDRAW_FIELD)
583       DrawMaskedBorderExt_FIELD(draw_target);
584     if (redraw_mask & REDRAW_DOOR_1)
585       DrawMaskedBorderExt_DOOR_1(draw_target);
586     if (redraw_mask & REDRAW_DOOR_2)
587       DrawMaskedBorderExt_DOOR_2(draw_target);
588     if (redraw_mask & REDRAW_DOOR_3)
589       DrawMaskedBorderExt_DOOR_3(draw_target);
590   }
591 }
592
593 void DrawMaskedBorder_FIELD(void)
594 {
595   DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
596 }
597
598 void DrawMaskedBorder(int redraw_mask)
599 {
600   DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
601 }
602
603 void DrawMaskedBorderToTarget(int draw_target)
604 {
605   if (draw_target == DRAW_TO_BACKBUFFER ||
606       draw_target == DRAW_TO_SCREEN)
607   {
608     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
609   }
610   else
611   {
612     int last_border_status = global.border_status;
613
614     if (draw_target == DRAW_TO_FADE_SOURCE)
615     {
616       global.border_status = gfx.fade_border_source_status;
617       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
618     }
619     else if (draw_target == DRAW_TO_FADE_TARGET)
620     {
621       global.border_status = gfx.fade_border_target_status;
622       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
623     }
624
625     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
626
627     global.border_status = last_border_status;
628     gfx.masked_border_bitmap_ptr = backbuffer;
629   }
630 }
631
632 void DrawTileCursor(int draw_target)
633 {
634   DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
635 }
636
637 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
638 {
639   BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
640 }
641
642 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
643 {
644   int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
645   int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
646
647   BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
648 }
649
650 void BlitScreenToBitmap(Bitmap *target_bitmap)
651 {
652   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
653     BlitScreenToBitmap_EM(target_bitmap);
654   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
655     BlitScreenToBitmap_SP(target_bitmap);
656   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
657     BlitScreenToBitmap_MM(target_bitmap);
658   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
659     BlitScreenToBitmap_RND(target_bitmap);
660
661   redraw_mask |= REDRAW_FIELD;
662 }
663
664 static void DrawFramesPerSecond(void)
665 {
666   char text[100];
667   int font_nr = FONT_TEXT_2;
668   int font_width = getFontWidth(font_nr);
669   int draw_deactivation_mask = GetDrawDeactivationMask();
670   boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
671
672   // draw FPS with leading space (needed if field buffer deactivated)
673   sprintf(text, " %04.1f fps", global.frames_per_second);
674
675   // override draw deactivation mask (required for invisible warp mode)
676   SetDrawDeactivationMask(REDRAW_NONE);
677
678   // draw opaque FPS if field buffer deactivated, else draw masked FPS
679   DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
680               font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
681
682   // set draw deactivation mask to previous value
683   SetDrawDeactivationMask(draw_deactivation_mask);
684
685   // force full-screen redraw in this frame
686   redraw_mask = REDRAW_ALL;
687 }
688
689 #if DEBUG_FRAME_TIME
690 static void PrintFrameTimeDebugging(void)
691 {
692   static unsigned int last_counter = 0;
693   unsigned int counter = Counter();
694   int diff_1 = counter - last_counter;
695   int diff_2 = diff_1 - GAME_FRAME_DELAY;
696   int diff_2_max = 20;
697   int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
698   char diff_bar[2 * diff_2_max + 5];
699   int pos = 0;
700   int i;
701
702   diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
703
704   for (i = 0; i < diff_2_max; i++)
705     diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
706                        i >= diff_2_max - diff_2_cut ? '-' : ' ');
707
708   diff_bar[pos++] = '|';
709
710   for (i = 0; i < diff_2_max; i++)
711     diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
712
713   diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
714
715   diff_bar[pos++] = '\0';
716
717   Debug("time:frame", "%06d [%02d] [%c%02d] %s",
718         counter,
719         diff_1,
720         (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
721         diff_bar);
722
723   last_counter = counter;
724 }
725 #endif
726
727 static int unifiedRedrawMask(int mask)
728 {
729   if (mask & REDRAW_ALL)
730     return REDRAW_ALL;
731
732   if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
733     return REDRAW_ALL;
734
735   return mask;
736 }
737
738 static boolean equalRedrawMasks(int mask_1, int mask_2)
739 {
740   return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
741 }
742
743 void BackToFront(void)
744 {
745   static int last_redraw_mask = REDRAW_NONE;
746
747   // force screen redraw in every frame to continue drawing global animations
748   // (but always use the last redraw mask to prevent unwanted side effects)
749   if (redraw_mask == REDRAW_NONE)
750     redraw_mask = last_redraw_mask;
751
752   last_redraw_mask = redraw_mask;
753
754 #if 1
755   // masked border now drawn immediately when blitting backbuffer to window
756 #else
757   // draw masked border to all viewports, if defined
758   DrawMaskedBorder(redraw_mask);
759 #endif
760
761   // draw frames per second (only if debug mode is enabled)
762   if (redraw_mask & REDRAW_FPS)
763     DrawFramesPerSecond();
764
765   // remove playfield redraw before potentially merging with doors redraw
766   if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
767     redraw_mask &= ~REDRAW_FIELD;
768
769   // redraw complete window if both playfield and (some) doors need redraw
770   if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
771     redraw_mask = REDRAW_ALL;
772
773   /* although redrawing the whole window would be fine for normal gameplay,
774      being able to only redraw the playfield is required for deactivating
775      certain drawing areas (mainly playfield) to work, which is needed for
776      warp-forward to be fast enough (by skipping redraw of most frames) */
777
778   if (redraw_mask & REDRAW_ALL)
779   {
780     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
781   }
782   else if (redraw_mask & REDRAW_FIELD)
783   {
784     BlitBitmap(backbuffer, window,
785                REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
786   }
787   else if (redraw_mask & REDRAW_DOORS)
788   {
789     // merge door areas to prevent calling screen redraw more than once
790     int x1 = WIN_XSIZE;
791     int y1 = WIN_YSIZE;
792     int x2 = 0;
793     int y2 = 0;
794
795     if (redraw_mask & REDRAW_DOOR_1)
796     {
797       x1 = MIN(x1, DX);
798       y1 = MIN(y1, DY);
799       x2 = MAX(x2, DX + DXSIZE);
800       y2 = MAX(y2, DY + DYSIZE);
801     }
802
803     if (redraw_mask & REDRAW_DOOR_2)
804     {
805       x1 = MIN(x1, VX);
806       y1 = MIN(y1, VY);
807       x2 = MAX(x2, VX + VXSIZE);
808       y2 = MAX(y2, VY + VYSIZE);
809     }
810
811     if (redraw_mask & REDRAW_DOOR_3)
812     {
813       x1 = MIN(x1, EX);
814       y1 = MIN(y1, EY);
815       x2 = MAX(x2, EX + EXSIZE);
816       y2 = MAX(y2, EY + EYSIZE);
817     }
818
819     // make sure that at least one pixel is blitted, and inside the screen
820     // (else nothing is blitted, causing the animations not to be updated)
821     x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
822     y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
823     x2 = MIN(MAX(1, x2), WIN_XSIZE);
824     y2 = MIN(MAX(1, y2), WIN_YSIZE);
825
826     BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
827   }
828
829   redraw_mask = REDRAW_NONE;
830
831 #if DEBUG_FRAME_TIME
832   PrintFrameTimeDebugging();
833 #endif
834 }
835
836 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
837 {
838   unsigned int frame_delay_value_old = GetVideoFrameDelay();
839
840   SetVideoFrameDelay(frame_delay_value);
841
842   BackToFront();
843
844   SetVideoFrameDelay(frame_delay_value_old);
845 }
846
847 static int fade_type_skip = FADE_TYPE_NONE;
848
849 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
850 {
851   void (*draw_border_function)(void) = NULL;
852   int x, y, width, height;
853   int fade_delay, post_delay;
854
855   if (fade_type == FADE_TYPE_FADE_OUT)
856   {
857     if (fade_type_skip != FADE_TYPE_NONE)
858     {
859       // skip all fade operations until specified fade operation
860       if (fade_type & fade_type_skip)
861         fade_type_skip = FADE_TYPE_NONE;
862
863       return;
864     }
865
866     if (fading.fade_mode & FADE_TYPE_TRANSFORM)
867       return;
868   }
869
870   redraw_mask |= fade_mask;
871
872   if (fade_type == FADE_TYPE_SKIP)
873   {
874     fade_type_skip = fade_mode;
875
876     return;
877   }
878
879   fade_delay = fading.fade_delay;
880   post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
881
882   if (fade_type_skip != FADE_TYPE_NONE)
883   {
884     // skip all fade operations until specified fade operation
885     if (fade_type & fade_type_skip)
886       fade_type_skip = FADE_TYPE_NONE;
887
888     fade_delay = 0;
889   }
890
891   if (global.autoplay_leveldir)
892   {
893     return;
894   }
895
896   if (fade_mask == REDRAW_FIELD)
897   {
898     x = FADE_SX;
899     y = FADE_SY;
900     width  = FADE_SXSIZE;
901     height = FADE_SYSIZE;
902
903     if (border.draw_masked_when_fading)
904       draw_border_function = DrawMaskedBorder_FIELD;    // update when fading
905     else
906       DrawMaskedBorder_FIELD();                         // draw once
907   }
908   else          // REDRAW_ALL
909   {
910     x = 0;
911     y = 0;
912     width  = WIN_XSIZE;
913     height = WIN_YSIZE;
914   }
915
916   // when switching screens without fading, set fade delay to zero
917   if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
918     fade_delay = 0;
919
920   // do not display black frame when fading out without fade delay
921   if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
922     return;
923
924   FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
925                 draw_border_function);
926
927   redraw_mask &= ~fade_mask;
928
929   ClearAutoRepeatKeyEvents();
930 }
931
932 static void SetScreenStates_BeforeFadingIn(void)
933 {
934   // temporarily set screen mode for animations to screen after fading in
935   global.anim_status = global.anim_status_next;
936
937   // store backbuffer with all animations that will be started after fading in
938   PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
939
940   // set screen mode for animations back to fading
941   global.anim_status = GAME_MODE_PSEUDO_FADING;
942 }
943
944 static void SetScreenStates_AfterFadingIn(void)
945 {
946   // store new source screen (to use correct masked border for fading)
947   gfx.fade_border_source_status = global.border_status;
948
949   global.anim_status = global.anim_status_next;
950 }
951
952 static void SetScreenStates_BeforeFadingOut(void)
953 {
954   // store new target screen (to use correct masked border for fading)
955   gfx.fade_border_target_status = game_status;
956
957   // set screen mode for animations to fading
958   global.anim_status = GAME_MODE_PSEUDO_FADING;
959
960   // store backbuffer with all animations that will be stopped for fading out
961   PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
962 }
963
964 static void SetScreenStates_AfterFadingOut(void)
965 {
966   global.border_status = game_status;
967 }
968
969 void FadeIn(int fade_mask)
970 {
971   SetScreenStates_BeforeFadingIn();
972
973 #if 1
974   DrawMaskedBorder(REDRAW_ALL);
975 #endif
976
977   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
978     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
979   else
980     FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
981
982   FADE_SX = REAL_SX;
983   FADE_SY = REAL_SY;
984   FADE_SXSIZE = FULL_SXSIZE;
985   FADE_SYSIZE = FULL_SYSIZE;
986
987   // activate virtual buttons depending on upcoming game status
988   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
989       game_status == GAME_MODE_PLAYING && !tape.playing)
990     SetOverlayActive(TRUE);
991
992   SetScreenStates_AfterFadingIn();
993
994   // force update of global animation status in case of rapid screen changes
995   redraw_mask = REDRAW_ALL;
996   BackToFront();
997 }
998
999 void FadeOut(int fade_mask)
1000 {
1001   // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1002   if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1003       fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1004     BackToFront();
1005
1006   SetScreenStates_BeforeFadingOut();
1007
1008   SetTileCursorActive(FALSE);
1009   SetOverlayActive(FALSE);
1010
1011 #if 0
1012   DrawMaskedBorder(REDRAW_ALL);
1013 #endif
1014
1015   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1016     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1017   else
1018     FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1019
1020   SetScreenStates_AfterFadingOut();
1021 }
1022
1023 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1024 {
1025   static struct TitleFadingInfo fading_leave_stored;
1026
1027   if (set)
1028     fading_leave_stored = fading_leave;
1029   else
1030     fading = fading_leave_stored;
1031 }
1032
1033 void FadeSetEnterMenu(void)
1034 {
1035   fading = menu.enter_menu;
1036
1037   FadeSetLeaveNext(fading, TRUE);       // (keep same fade mode)
1038 }
1039
1040 void FadeSetLeaveMenu(void)
1041 {
1042   fading = menu.leave_menu;
1043
1044   FadeSetLeaveNext(fading, TRUE);       // (keep same fade mode)
1045 }
1046
1047 void FadeSetEnterScreen(void)
1048 {
1049   fading = menu.enter_screen[game_status];
1050
1051   FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);       // store
1052 }
1053
1054 void FadeSetNextScreen(void)
1055 {
1056   fading = menu.next_screen[game_status];
1057
1058   // (do not overwrite fade mode set by FadeSetEnterScreen)
1059   // FadeSetLeaveNext(fading, TRUE);    // (keep same fade mode)
1060 }
1061
1062 void FadeSetLeaveScreen(void)
1063 {
1064   FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);      // recall
1065 }
1066
1067 void FadeSetFromType(int type)
1068 {
1069   if (type & TYPE_ENTER_SCREEN)
1070     FadeSetEnterScreen();
1071   else if (type & TYPE_ENTER)
1072     FadeSetEnterMenu();
1073   else if (type & TYPE_LEAVE)
1074     FadeSetLeaveMenu();
1075 }
1076
1077 void FadeSetDisabled(void)
1078 {
1079   static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1080
1081   fading = fading_none;
1082 }
1083
1084 void FadeSkipNextFadeIn(void)
1085 {
1086   FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1087 }
1088
1089 void FadeSkipNextFadeOut(void)
1090 {
1091   FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1092 }
1093
1094 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1095 {
1096   if (graphic == IMG_UNDEFINED)
1097     return NULL;
1098
1099   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1100
1101   return (graphic_info[graphic].bitmap != NULL || redefined ?
1102           graphic_info[graphic].bitmap :
1103           graphic_info[default_graphic].bitmap);
1104 }
1105
1106 static Bitmap *getBackgroundBitmap(int graphic)
1107 {
1108   return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1109 }
1110
1111 static Bitmap *getGlobalBorderBitmap(int graphic)
1112 {
1113   return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1114 }
1115
1116 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1117 {
1118   int graphic =
1119     (status == GAME_MODE_MAIN ||
1120      status == GAME_MODE_PSEUDO_TYPENAME        ? IMG_GLOBAL_BORDER_MAIN :
1121      status == GAME_MODE_SCORES                 ? IMG_GLOBAL_BORDER_SCORES :
1122      status == GAME_MODE_EDITOR                 ? IMG_GLOBAL_BORDER_EDITOR :
1123      status == GAME_MODE_PLAYING                ? IMG_GLOBAL_BORDER_PLAYING :
1124      IMG_GLOBAL_BORDER);
1125
1126   return getGlobalBorderBitmap(graphic);
1127 }
1128
1129 void SetWindowBackgroundImageIfDefined(int graphic)
1130 {
1131   if (graphic_info[graphic].bitmap)
1132     SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1133 }
1134
1135 void SetMainBackgroundImageIfDefined(int graphic)
1136 {
1137   if (graphic_info[graphic].bitmap)
1138     SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1139 }
1140
1141 void SetDoorBackgroundImageIfDefined(int graphic)
1142 {
1143   if (graphic_info[graphic].bitmap)
1144     SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1145 }
1146
1147 void SetWindowBackgroundImage(int graphic)
1148 {
1149   SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1150 }
1151
1152 void SetMainBackgroundImage(int graphic)
1153 {
1154   SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1155 }
1156
1157 void SetDoorBackgroundImage(int graphic)
1158 {
1159   SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1160 }
1161
1162 void SetPanelBackground(void)
1163 {
1164   struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1165
1166   BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1167                   gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1168
1169   SetDoorBackgroundBitmap(bitmap_db_panel);
1170 }
1171
1172 void DrawBackground(int x, int y, int width, int height)
1173 {
1174   // "drawto" might still point to playfield buffer here (hall of fame)
1175   ClearRectangleOnBackground(backbuffer, x, y, width, height);
1176
1177   if (IN_GFX_FIELD_FULL(x, y))
1178     redraw_mask |= REDRAW_FIELD;
1179   else if (IN_GFX_DOOR_1(x, y))
1180     redraw_mask |= REDRAW_DOOR_1;
1181   else if (IN_GFX_DOOR_2(x, y))
1182     redraw_mask |= REDRAW_DOOR_2;
1183   else if (IN_GFX_DOOR_3(x, y))
1184     redraw_mask |= REDRAW_DOOR_3;
1185 }
1186
1187 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1188 {
1189   struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1190
1191   if (font->bitmap == NULL)
1192     return;
1193
1194   DrawBackground(x, y, width, height);
1195 }
1196
1197 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1198 {
1199   struct GraphicInfo *g = &graphic_info[graphic];
1200
1201   if (g->bitmap == NULL)
1202     return;
1203
1204   DrawBackground(x, y, width, height);
1205 }
1206
1207 static int game_status_last = -1;
1208 static Bitmap *global_border_bitmap_last = NULL;
1209 static Bitmap *global_border_bitmap = NULL;
1210 static int real_sx_last = -1, real_sy_last = -1;
1211 static int full_sxsize_last = -1, full_sysize_last = -1;
1212 static int dx_last = -1, dy_last = -1;
1213 static int dxsize_last = -1, dysize_last = -1;
1214 static int vx_last = -1, vy_last = -1;
1215 static int vxsize_last = -1, vysize_last = -1;
1216 static int ex_last = -1, ey_last = -1;
1217 static int exsize_last = -1, eysize_last = -1;
1218
1219 boolean CheckIfGlobalBorderHasChanged(void)
1220 {
1221   // if game status has not changed, global border has not changed either
1222   if (game_status == game_status_last)
1223     return FALSE;
1224
1225   // determine and store new global border bitmap for current game status
1226   global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1227
1228   return (global_border_bitmap_last != global_border_bitmap);
1229 }
1230
1231 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED             0
1232
1233 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1234 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1235 {
1236   // if game status has not changed, nothing has to be redrawn
1237   if (game_status == game_status_last)
1238     return FALSE;
1239
1240   // redraw if last screen was title screen
1241   if (game_status_last == GAME_MODE_TITLE)
1242     return TRUE;
1243
1244   // redraw if global screen border has changed
1245   if (CheckIfGlobalBorderHasChanged())
1246     return TRUE;
1247
1248   // redraw if position or size of playfield area has changed
1249   if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1250       full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1251     return TRUE;
1252
1253   // redraw if position or size of door area has changed
1254   if (dx_last != DX || dy_last != DY ||
1255       dxsize_last != DXSIZE || dysize_last != DYSIZE)
1256     return TRUE;
1257
1258   // redraw if position or size of tape area has changed
1259   if (vx_last != VX || vy_last != VY ||
1260       vxsize_last != VXSIZE || vysize_last != VYSIZE)
1261     return TRUE;
1262
1263   // redraw if position or size of editor area has changed
1264   if (ex_last != EX || ey_last != EY ||
1265       exsize_last != EXSIZE || eysize_last != EYSIZE)
1266     return TRUE;
1267
1268   return FALSE;
1269 }
1270 #endif
1271
1272 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1273 {
1274   if (bitmap)
1275     BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1276   else
1277     ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1278 }
1279
1280 void RedrawGlobalBorder(void)
1281 {
1282   Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1283
1284   RedrawGlobalBorderFromBitmap(bitmap);
1285
1286   redraw_mask = REDRAW_ALL;
1287 }
1288
1289 static void RedrawGlobalBorderIfNeeded(void)
1290 {
1291 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1292   if (game_status == game_status_last)
1293     return;
1294 #endif
1295
1296   // copy current draw buffer to later copy back areas that have not changed
1297   if (game_status_last != GAME_MODE_TITLE)
1298     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1299
1300 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1301   if (CheckIfGlobalBorderRedrawIsNeeded())
1302 #else
1303   // determine and store new global border bitmap for current game status
1304   global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1305 #endif
1306   {
1307     // redraw global screen border (or clear, if defined to be empty)
1308     RedrawGlobalBorderFromBitmap(global_border_bitmap);
1309
1310     if (game_status == GAME_MODE_EDITOR)
1311       DrawSpecialEditorDoor();
1312
1313     // copy previous playfield and door areas, if they are defined on both
1314     // previous and current screen and if they still have the same size
1315
1316     if (real_sx_last != -1 && real_sy_last != -1 &&
1317         REAL_SX != -1 && REAL_SY != -1 &&
1318         full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1319       BlitBitmap(bitmap_db_store_1, backbuffer,
1320                  real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1321                  REAL_SX, REAL_SY);
1322
1323     if (dx_last != -1 && dy_last != -1 &&
1324         DX != -1 && DY != -1 &&
1325         dxsize_last == DXSIZE && dysize_last == DYSIZE)
1326       BlitBitmap(bitmap_db_store_1, backbuffer,
1327                  dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1328
1329     if (game_status != GAME_MODE_EDITOR)
1330     {
1331       if (vx_last != -1 && vy_last != -1 &&
1332           VX != -1 && VY != -1 &&
1333           vxsize_last == VXSIZE && vysize_last == VYSIZE)
1334         BlitBitmap(bitmap_db_store_1, backbuffer,
1335                    vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1336     }
1337     else
1338     {
1339       if (ex_last != -1 && ey_last != -1 &&
1340           EX != -1 && EY != -1 &&
1341           exsize_last == EXSIZE && eysize_last == EYSIZE)
1342         BlitBitmap(bitmap_db_store_1, backbuffer,
1343                    ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1344     }
1345
1346     redraw_mask = REDRAW_ALL;
1347   }
1348
1349   game_status_last = game_status;
1350
1351   global_border_bitmap_last = global_border_bitmap;
1352
1353   real_sx_last = REAL_SX;
1354   real_sy_last = REAL_SY;
1355   full_sxsize_last = FULL_SXSIZE;
1356   full_sysize_last = FULL_SYSIZE;
1357   dx_last = DX;
1358   dy_last = DY;
1359   dxsize_last = DXSIZE;
1360   dysize_last = DYSIZE;
1361   vx_last = VX;
1362   vy_last = VY;
1363   vxsize_last = VXSIZE;
1364   vysize_last = VYSIZE;
1365   ex_last = EX;
1366   ey_last = EY;
1367   exsize_last = EXSIZE;
1368   eysize_last = EYSIZE;
1369 }
1370
1371 void ClearField(void)
1372 {
1373   RedrawGlobalBorderIfNeeded();
1374
1375   // !!! "drawto" might still point to playfield buffer here (see above) !!!
1376   // (when entering hall of fame after playing)
1377   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1378
1379   // !!! maybe this should be done before clearing the background !!!
1380   if (game_status == GAME_MODE_PLAYING)
1381   {
1382     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1383     SetDrawtoField(DRAW_TO_FIELDBUFFER);
1384   }
1385   else
1386   {
1387     SetDrawtoField(DRAW_TO_BACKBUFFER);
1388   }
1389 }
1390
1391 void MarkTileDirty(int x, int y)
1392 {
1393   redraw_mask |= REDRAW_FIELD;
1394 }
1395
1396 void SetBorderElement(void)
1397 {
1398   int x, y;
1399
1400   BorderElement = EL_EMPTY;
1401
1402   // only the R'n'D game engine may use an additional steelwall border
1403   if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1404     return;
1405
1406   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1407   {
1408     for (x = 0; x < lev_fieldx; x++)
1409     {
1410       if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1411         BorderElement = EL_STEELWALL;
1412
1413       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1414         x = lev_fieldx - 2;
1415     }
1416   }
1417 }
1418
1419 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1420                        int max_array_fieldx, int max_array_fieldy,
1421                        short field[max_array_fieldx][max_array_fieldy],
1422                        int max_fieldx, int max_fieldy)
1423 {
1424   int i,x,y;
1425   int old_element;
1426   static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1427   static int safety = 0;
1428
1429   // check if starting field still has the desired content
1430   if (field[from_x][from_y] == fill_element)
1431     return;
1432
1433   safety++;
1434
1435   if (safety > max_fieldx * max_fieldy)
1436     Fail("Something went wrong in 'FloodFill()'. Please debug.");
1437
1438   old_element = field[from_x][from_y];
1439   field[from_x][from_y] = fill_element;
1440
1441   for (i = 0; i < 4; i++)
1442   {
1443     x = from_x + check[i][0];
1444     y = from_y + check[i][1];
1445
1446     if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1447       FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1448                         field, max_fieldx, max_fieldy);
1449   }
1450
1451   safety--;
1452 }
1453
1454 void FloodFillLevel(int from_x, int from_y, int fill_element,
1455                     short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1456                     int max_fieldx, int max_fieldy)
1457 {
1458   FloodFillLevelExt(from_x, from_y, fill_element,
1459                     MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1460                     max_fieldx, max_fieldy);
1461 }
1462
1463 void SetRandomAnimationValue(int x, int y)
1464 {
1465   gfx.anim_random_frame = GfxRandom[x][y];
1466 }
1467
1468 int getGraphicAnimationFrame(int graphic, int sync_frame)
1469 {
1470   // animation synchronized with global frame counter, not move position
1471   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1472     sync_frame = FrameCounter;
1473
1474   return getAnimationFrame(graphic_info[graphic].anim_frames,
1475                            graphic_info[graphic].anim_delay,
1476                            graphic_info[graphic].anim_mode,
1477                            graphic_info[graphic].anim_start_frame,
1478                            sync_frame);
1479 }
1480
1481 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1482 {
1483   struct GraphicInfo *g = &graphic_info[graphic];
1484   int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1485
1486   if (tilesize == gfx.standard_tile_size)
1487     *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1488   else if (tilesize == game.tile_size)
1489     *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1490   else
1491     *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1492 }
1493
1494 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1495                         boolean get_backside)
1496 {
1497   struct GraphicInfo *g = &graphic_info[graphic];
1498   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1499   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1500
1501   if (g->offset_y == 0)         // frames are ordered horizontally
1502   {
1503     int max_width = g->anim_frames_per_line * g->width;
1504     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1505
1506     *x = pos % max_width;
1507     *y = src_y % g->height + pos / max_width * g->height;
1508   }
1509   else if (g->offset_x == 0)    // frames are ordered vertically
1510   {
1511     int max_height = g->anim_frames_per_line * g->height;
1512     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1513
1514     *x = src_x % g->width + pos / max_height * g->width;
1515     *y = pos % max_height;
1516   }
1517   else                          // frames are ordered diagonally
1518   {
1519     *x = src_x + frame * g->offset_x;
1520     *y = src_y + frame * g->offset_y;
1521   }
1522 }
1523
1524 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1525                               Bitmap **bitmap, int *x, int *y,
1526                               boolean get_backside)
1527 {
1528   struct GraphicInfo *g = &graphic_info[graphic];
1529
1530   // if no graphics defined at all, use fallback graphics
1531   if (g->bitmaps == NULL)
1532     *g = graphic_info[IMG_CHAR_EXCLAM];
1533
1534   // if no in-game graphics defined, always use standard graphic size
1535   if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1536     tilesize = TILESIZE;
1537
1538   getGraphicSourceBitmap(graphic, tilesize, bitmap);
1539   getGraphicSourceXY(graphic, frame, x, y, get_backside);
1540
1541   *x = *x * tilesize / g->tile_size;
1542   *y = *y * tilesize / g->tile_size;
1543 }
1544
1545 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1546                            Bitmap **bitmap, int *x, int *y)
1547 {
1548   getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1549 }
1550
1551 void getFixedGraphicSource(int graphic, int frame,
1552                            Bitmap **bitmap, int *x, int *y)
1553 {
1554   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1555 }
1556
1557 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1558 {
1559   getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1560 }
1561
1562 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1563                                 int *x, int *y, boolean get_backside)
1564 {
1565   getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1566                            get_backside);
1567 }
1568
1569 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1570 {
1571   getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1572 }
1573
1574 void DrawGraphic(int x, int y, int graphic, int frame)
1575 {
1576 #if DEBUG
1577   if (!IN_SCR_FIELD(x, y))
1578   {
1579     Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1580     Debug("draw:DrawGraphic", "This should never happen!");
1581
1582     return;
1583   }
1584 #endif
1585
1586   DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1587                  frame);
1588
1589   MarkTileDirty(x, y);
1590 }
1591
1592 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1593 {
1594 #if DEBUG
1595   if (!IN_SCR_FIELD(x, y))
1596   {
1597     Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1598           x, y, graphic);
1599     Debug("draw:DrawFixedGraphic", "This should never happen!");
1600
1601     return;
1602   }
1603 #endif
1604
1605   DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1606                       frame);
1607   MarkTileDirty(x, y);
1608 }
1609
1610 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1611                     int frame)
1612 {
1613   Bitmap *src_bitmap;
1614   int src_x, src_y;
1615
1616   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1617
1618   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1619 }
1620
1621 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1622                          int frame)
1623 {
1624   Bitmap *src_bitmap;
1625   int src_x, src_y;
1626
1627   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1628   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1629 }
1630
1631 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1632 {
1633 #if DEBUG
1634   if (!IN_SCR_FIELD(x, y))
1635   {
1636     Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1637           x, y, graphic);
1638     Debug("draw:DrawGraphicThruMask", "This should never happen!");
1639
1640     return;
1641   }
1642 #endif
1643
1644   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1645                          graphic, frame);
1646
1647   MarkTileDirty(x, y);
1648 }
1649
1650 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1651 {
1652 #if DEBUG
1653   if (!IN_SCR_FIELD(x, y))
1654   {
1655     Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1656           x, y, graphic);
1657     Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1658
1659     return;
1660   }
1661 #endif
1662
1663   DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1664                               graphic, frame);
1665   MarkTileDirty(x, y);
1666 }
1667
1668 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1669                             int frame)
1670 {
1671   Bitmap *src_bitmap;
1672   int src_x, src_y;
1673
1674   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1675
1676   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1677                    dst_x, dst_y);
1678 }
1679
1680 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1681                                  int graphic, int frame)
1682 {
1683   Bitmap *src_bitmap;
1684   int src_x, src_y;
1685
1686   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1687
1688   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1689                    dst_x, dst_y);
1690 }
1691
1692 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1693 {
1694   DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1695                       frame, tilesize);
1696   MarkTileDirty(x / tilesize, y / tilesize);
1697 }
1698
1699 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1700                               int tilesize)
1701 {
1702   DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1703                               graphic, frame, tilesize);
1704   MarkTileDirty(x / tilesize, y / tilesize);
1705 }
1706
1707 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1708                          int tilesize)
1709 {
1710   Bitmap *src_bitmap;
1711   int src_x, src_y;
1712
1713   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1714   BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1715 }
1716
1717 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1718                                  int frame, int tilesize)
1719 {
1720   Bitmap *src_bitmap;
1721   int src_x, src_y;
1722
1723   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1724   BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1725 }
1726
1727 void DrawMiniGraphic(int x, int y, int graphic)
1728 {
1729   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1730   MarkTileDirty(x / 2, y / 2);
1731 }
1732
1733 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1734 {
1735   Bitmap *src_bitmap;
1736   int src_x, src_y;
1737
1738   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1739   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1740 }
1741
1742 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1743                                      int graphic, int frame,
1744                                      int cut_mode, int mask_mode)
1745 {
1746   Bitmap *src_bitmap;
1747   int src_x, src_y;
1748   int dst_x, dst_y;
1749   int width = TILEX, height = TILEY;
1750   int cx = 0, cy = 0;
1751
1752   if (dx || dy)                 // shifted graphic
1753   {
1754     if (x < BX1)                // object enters playfield from the left
1755     {
1756       x = BX1;
1757       width = dx;
1758       cx = TILEX - dx;
1759       dx = 0;
1760     }
1761     else if (x > BX2)           // object enters playfield from the right
1762     {
1763       x = BX2;
1764       width = -dx;
1765       dx = TILEX + dx;
1766     }
1767     else if (x == BX1 && dx < 0) // object leaves playfield to the left
1768     {
1769       width += dx;
1770       cx = -dx;
1771       dx = 0;
1772     }
1773     else if (x == BX2 && dx > 0) // object leaves playfield to the right
1774       width -= dx;
1775     else if (dx)                // general horizontal movement
1776       MarkTileDirty(x + SIGN(dx), y);
1777
1778     if (y < BY1)                // object enters playfield from the top
1779     {
1780       if (cut_mode == CUT_BELOW) // object completely above top border
1781         return;
1782
1783       y = BY1;
1784       height = dy;
1785       cy = TILEY - dy;
1786       dy = 0;
1787     }
1788     else if (y > BY2)           // object enters playfield from the bottom
1789     {
1790       y = BY2;
1791       height = -dy;
1792       dy = TILEY + dy;
1793     }
1794     else if (y == BY1 && dy < 0) // object leaves playfield to the top
1795     {
1796       height += dy;
1797       cy = -dy;
1798       dy = 0;
1799     }
1800     else if (dy > 0 && cut_mode == CUT_ABOVE)
1801     {
1802       if (y == BY2)             // object completely above bottom border
1803         return;
1804
1805       height = dy;
1806       cy = TILEY - dy;
1807       dy = TILEY;
1808       MarkTileDirty(x, y + 1);
1809     }                           // object leaves playfield to the bottom
1810     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1811       height -= dy;
1812     else if (dy)                // general vertical movement
1813       MarkTileDirty(x, y + SIGN(dy));
1814   }
1815
1816 #if DEBUG
1817   if (!IN_SCR_FIELD(x, y))
1818   {
1819     Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1820           x, y, graphic);
1821     Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1822
1823     return;
1824   }
1825 #endif
1826
1827   width = width * TILESIZE_VAR / TILESIZE;
1828   height = height * TILESIZE_VAR / TILESIZE;
1829   cx = cx * TILESIZE_VAR / TILESIZE;
1830   cy = cy * TILESIZE_VAR / TILESIZE;
1831   dx = dx * TILESIZE_VAR / TILESIZE;
1832   dy = dy * TILESIZE_VAR / TILESIZE;
1833
1834   if (width > 0 && height > 0)
1835   {
1836     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1837
1838     src_x += cx;
1839     src_y += cy;
1840
1841     dst_x = FX + x * TILEX_VAR + dx;
1842     dst_y = FY + y * TILEY_VAR + dy;
1843
1844     if (mask_mode == USE_MASKING)
1845       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1846                        dst_x, dst_y);
1847     else
1848       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1849                  dst_x, dst_y);
1850
1851     MarkTileDirty(x, y);
1852   }
1853 }
1854
1855 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1856                                      int graphic, int frame,
1857                                      int cut_mode, int mask_mode)
1858 {
1859   Bitmap *src_bitmap;
1860   int src_x, src_y;
1861   int dst_x, dst_y;
1862   int width = TILEX_VAR, height = TILEY_VAR;
1863   int x1 = x;
1864   int y1 = y;
1865   int x2 = x + SIGN(dx);
1866   int y2 = y + SIGN(dy);
1867
1868   // movement with two-tile animations must be sync'ed with movement position,
1869   // not with current GfxFrame (which can be higher when using slow movement)
1870   int anim_pos = (dx ? ABS(dx) : ABS(dy));
1871   int anim_frames = graphic_info[graphic].anim_frames;
1872
1873   // (we also need anim_delay here for movement animations with less frames)
1874   int anim_delay = graphic_info[graphic].anim_delay;
1875   int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1876
1877   boolean draw_start_tile = (cut_mode != CUT_ABOVE);    // only for falling!
1878   boolean draw_end_tile   = (cut_mode != CUT_BELOW);    // only for falling!
1879
1880   // re-calculate animation frame for two-tile movement animation
1881   frame = getGraphicAnimationFrame(graphic, sync_frame);
1882
1883   // check if movement start graphic inside screen area and should be drawn
1884   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1885   {
1886     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1887
1888     dst_x = FX + x1 * TILEX_VAR;
1889     dst_y = FY + y1 * TILEY_VAR;
1890
1891     if (mask_mode == USE_MASKING)
1892       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1893                        dst_x, dst_y);
1894     else
1895       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1896                  dst_x, dst_y);
1897
1898     MarkTileDirty(x1, y1);
1899   }
1900
1901   // check if movement end graphic inside screen area and should be drawn
1902   if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1903   {
1904     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1905
1906     dst_x = FX + x2 * TILEX_VAR;
1907     dst_y = FY + y2 * TILEY_VAR;
1908
1909     if (mask_mode == USE_MASKING)
1910       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1911                        dst_x, dst_y);
1912     else
1913       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1914                  dst_x, dst_y);
1915
1916     MarkTileDirty(x2, y2);
1917   }
1918 }
1919
1920 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1921                                int graphic, int frame,
1922                                int cut_mode, int mask_mode)
1923 {
1924   if (graphic < 0)
1925   {
1926     DrawGraphic(x, y, graphic, frame);
1927
1928     return;
1929   }
1930
1931   if (graphic_info[graphic].double_movement)    // EM style movement images
1932     DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1933   else
1934     DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1935 }
1936
1937 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1938                                        int graphic, int frame, int cut_mode)
1939 {
1940   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1941 }
1942
1943 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1944                           int cut_mode, int mask_mode)
1945 {
1946   int lx = LEVELX(x), ly = LEVELY(y);
1947   int graphic;
1948   int frame;
1949
1950   if (IN_LEV_FIELD(lx, ly))
1951   {
1952     SetRandomAnimationValue(lx, ly);
1953
1954     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1955     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1956
1957     // do not use double (EM style) movement graphic when not moving
1958     if (graphic_info[graphic].double_movement && !dx && !dy)
1959     {
1960       graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1961       frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1962     }
1963   }
1964   else  // border element
1965   {
1966     graphic = el2img(element);
1967     frame = getGraphicAnimationFrame(graphic, -1);
1968   }
1969
1970   if (element == EL_EXPANDABLE_WALL)
1971   {
1972     boolean left_stopped = FALSE, right_stopped = FALSE;
1973
1974     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
1975       left_stopped = TRUE;
1976     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
1977       right_stopped = TRUE;
1978
1979     if (left_stopped && right_stopped)
1980       graphic = IMG_WALL;
1981     else if (left_stopped)
1982     {
1983       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1984       frame = graphic_info[graphic].anim_frames - 1;
1985     }
1986     else if (right_stopped)
1987     {
1988       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1989       frame = graphic_info[graphic].anim_frames - 1;
1990     }
1991   }
1992
1993   if (dx || dy)
1994     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1995   else if (mask_mode == USE_MASKING)
1996     DrawGraphicThruMask(x, y, graphic, frame);
1997   else
1998     DrawGraphic(x, y, graphic, frame);
1999 }
2000
2001 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2002                          int cut_mode, int mask_mode)
2003 {
2004   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2005     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2006                          cut_mode, mask_mode);
2007 }
2008
2009 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2010                               int cut_mode)
2011 {
2012   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2013 }
2014
2015 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2016                              int cut_mode)
2017 {
2018   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2019 }
2020
2021 void DrawLevelElementThruMask(int x, int y, int element)
2022 {
2023   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2024 }
2025
2026 void DrawLevelFieldThruMask(int x, int y)
2027 {
2028   DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2029 }
2030
2031 // !!! implementation of quicksand is totally broken !!!
2032 #define IS_CRUMBLED_TILE(x, y, e)                                       \
2033         (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
2034                              !IS_MOVING(x, y) ||                        \
2035                              (e) == EL_QUICKSAND_EMPTYING ||            \
2036                              (e) == EL_QUICKSAND_FAST_EMPTYING))
2037
2038 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2039                                                int graphic)
2040 {
2041   Bitmap *src_bitmap;
2042   int src_x, src_y;
2043   int width, height, cx, cy;
2044   int sx = SCREENX(x), sy = SCREENY(y);
2045   int crumbled_border_size = graphic_info[graphic].border_size;
2046   int crumbled_tile_size = graphic_info[graphic].tile_size;
2047   int crumbled_border_size_var =
2048     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2049   int i;
2050
2051   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2052
2053   for (i = 1; i < 4; i++)
2054   {
2055     int dxx = (i & 1 ? dx : 0);
2056     int dyy = (i & 2 ? dy : 0);
2057     int xx = x + dxx;
2058     int yy = y + dyy;
2059     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2060                    BorderElement);
2061
2062     // check if neighbour field is of same crumble type
2063     boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2064                     graphic_info[graphic].class ==
2065                     graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2066
2067     // return if check prevents inner corner
2068     if (same == (dxx == dx && dyy == dy))
2069       return;
2070   }
2071
2072   // if we reach this point, we have an inner corner
2073
2074   getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2075
2076   width  = crumbled_border_size_var;
2077   height = crumbled_border_size_var;
2078   cx = (dx > 0 ? TILESIZE_VAR - width  : 0);
2079   cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2080
2081   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2082              width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2083 }
2084
2085 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2086                                           int dir)
2087 {
2088   Bitmap *src_bitmap;
2089   int src_x, src_y;
2090   int width, height, bx, by, cx, cy;
2091   int sx = SCREENX(x), sy = SCREENY(y);
2092   int crumbled_border_size = graphic_info[graphic].border_size;
2093   int crumbled_tile_size = graphic_info[graphic].tile_size;
2094   int crumbled_border_size_var =
2095     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2096   int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2097   int i;
2098
2099   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2100
2101   // draw simple, sloppy, non-corner-accurate crumbled border
2102
2103   width  = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2104   height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2105   cx = (dir == 2 ? crumbled_border_pos_var : 0);
2106   cy = (dir == 3 ? crumbled_border_pos_var : 0);
2107
2108   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2109              FX + sx * TILEX_VAR + cx,
2110              FY + sy * TILEY_VAR + cy);
2111
2112   // (remaining middle border part must be at least as big as corner part)
2113   if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2114       crumbled_border_size_var >= TILESIZE_VAR / 3)
2115     return;
2116
2117   // correct corners of crumbled border, if needed
2118
2119   for (i = -1; i <= 1; i += 2)
2120   {
2121     int xx = x + (dir == 0 || dir == 3 ? i : 0);
2122     int yy = y + (dir == 1 || dir == 2 ? i : 0);
2123     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2124                    BorderElement);
2125
2126     // check if neighbour field is of same crumble type
2127     if (IS_CRUMBLED_TILE(xx, yy, element) &&
2128         graphic_info[graphic].class ==
2129         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2130     {
2131       // no crumbled corner, but continued crumbled border
2132
2133       int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2134       int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2135       int b1 = (i == 1 ? crumbled_border_size_var :
2136                 TILESIZE_VAR - 2 * crumbled_border_size_var);
2137
2138       width  = crumbled_border_size_var;
2139       height = crumbled_border_size_var;
2140
2141       if (dir == 1 || dir == 2)
2142       {
2143         cx = c1;
2144         cy = c2;
2145         bx = cx;
2146         by = b1;
2147       }
2148       else
2149       {
2150         cx = c2;
2151         cy = c1;
2152         bx = b1;
2153         by = cy;
2154       }
2155
2156       BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2157                  width, height,
2158                  FX + sx * TILEX_VAR + cx,
2159                  FY + sy * TILEY_VAR + cy);
2160     }
2161   }
2162 }
2163
2164 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2165 {
2166   int sx = SCREENX(x), sy = SCREENY(y);
2167   int element;
2168   int i;
2169   static int xy[4][2] =
2170   {
2171     { 0, -1 },
2172     { -1, 0 },
2173     { +1, 0 },
2174     { 0, +1 }
2175   };
2176
2177   if (!IN_LEV_FIELD(x, y))
2178     return;
2179
2180   element = TILE_GFX_ELEMENT(x, y);
2181
2182   if (IS_CRUMBLED_TILE(x, y, element))          // crumble field itself
2183   {
2184     if (!IN_SCR_FIELD(sx, sy))
2185       return;
2186
2187     // crumble field borders towards direct neighbour fields
2188     for (i = 0; i < 4; i++)
2189     {
2190       int xx = x + xy[i][0];
2191       int yy = y + xy[i][1];
2192
2193       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2194                  BorderElement);
2195
2196       // check if neighbour field is of same crumble type
2197       if (IS_CRUMBLED_TILE(xx, yy, element) &&
2198           graphic_info[graphic].class ==
2199           graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2200         continue;
2201
2202       DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2203     }
2204
2205     // crumble inner field corners towards corner neighbour fields
2206     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2207         graphic_info[graphic].anim_frames == 2)
2208     {
2209       for (i = 0; i < 4; i++)
2210       {
2211         int dx = (i & 1 ? +1 : -1);
2212         int dy = (i & 2 ? +1 : -1);
2213
2214         DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2215       }
2216     }
2217
2218     MarkTileDirty(sx, sy);
2219   }
2220   else          // center field is not crumbled -- crumble neighbour fields
2221   {
2222     // crumble field borders of direct neighbour fields
2223     for (i = 0; i < 4; i++)
2224     {
2225       int xx = x + xy[i][0];
2226       int yy = y + xy[i][1];
2227       int sxx = sx + xy[i][0];
2228       int syy = sy + xy[i][1];
2229
2230       if (!IN_LEV_FIELD(xx, yy) ||
2231           !IN_SCR_FIELD(sxx, syy))
2232         continue;
2233
2234       // do not crumble fields that are being digged or snapped
2235       if (Tile[xx][yy] == EL_EMPTY ||
2236           Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2237         continue;
2238
2239       element = TILE_GFX_ELEMENT(xx, yy);
2240
2241       if (!IS_CRUMBLED_TILE(xx, yy, element))
2242         continue;
2243
2244       graphic = el_act2crm(element, ACTION_DEFAULT);
2245
2246       DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2247
2248       MarkTileDirty(sxx, syy);
2249     }
2250
2251     // crumble inner field corners of corner neighbour fields
2252     for (i = 0; i < 4; i++)
2253     {
2254       int dx = (i & 1 ? +1 : -1);
2255       int dy = (i & 2 ? +1 : -1);
2256       int xx = x + dx;
2257       int yy = y + dy;
2258       int sxx = sx + dx;
2259       int syy = sy + dy;
2260
2261       if (!IN_LEV_FIELD(xx, yy) ||
2262           !IN_SCR_FIELD(sxx, syy))
2263         continue;
2264
2265       if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2266         continue;
2267
2268       element = TILE_GFX_ELEMENT(xx, yy);
2269
2270       if (!IS_CRUMBLED_TILE(xx, yy, element))
2271         continue;
2272
2273       graphic = el_act2crm(element, ACTION_DEFAULT);
2274
2275       if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2276           graphic_info[graphic].anim_frames == 2)
2277         DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2278
2279       MarkTileDirty(sxx, syy);
2280     }
2281   }
2282 }
2283
2284 void DrawLevelFieldCrumbled(int x, int y)
2285 {
2286   int graphic;
2287
2288   if (!IN_LEV_FIELD(x, y))
2289     return;
2290
2291   if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2292       GfxElement[x][y] != EL_UNDEFINED &&
2293       GFX_CRUMBLED(GfxElement[x][y]))
2294   {
2295     DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2296
2297     return;
2298   }
2299
2300   graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2301
2302   DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2303 }
2304
2305 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2306                                    int step_frame)
2307 {
2308   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2309   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2310   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2311   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2312   int sx = SCREENX(x), sy = SCREENY(y);
2313
2314   DrawGraphic(sx, sy, graphic1, frame1);
2315   DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2316 }
2317
2318 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2319 {
2320   int sx = SCREENX(x), sy = SCREENY(y);
2321   static int xy[4][2] =
2322   {
2323     { 0, -1 },
2324     { -1, 0 },
2325     { +1, 0 },
2326     { 0, +1 }
2327   };
2328   int i;
2329
2330   // crumble direct neighbour fields (required for field borders)
2331   for (i = 0; i < 4; i++)
2332   {
2333     int xx = x + xy[i][0];
2334     int yy = y + xy[i][1];
2335     int sxx = sx + xy[i][0];
2336     int syy = sy + xy[i][1];
2337
2338     if (!IN_LEV_FIELD(xx, yy) ||
2339         !IN_SCR_FIELD(sxx, syy) ||
2340         !GFX_CRUMBLED(Tile[xx][yy]) ||
2341         IS_MOVING(xx, yy))
2342       continue;
2343
2344     DrawLevelField(xx, yy);
2345   }
2346
2347   // crumble corner neighbour fields (required for inner field corners)
2348   for (i = 0; i < 4; i++)
2349   {
2350     int dx = (i & 1 ? +1 : -1);
2351     int dy = (i & 2 ? +1 : -1);
2352     int xx = x + dx;
2353     int yy = y + dy;
2354     int sxx = sx + dx;
2355     int syy = sy + dy;
2356
2357     if (!IN_LEV_FIELD(xx, yy) ||
2358         !IN_SCR_FIELD(sxx, syy) ||
2359         !GFX_CRUMBLED(Tile[xx][yy]) ||
2360         IS_MOVING(xx, yy))
2361       continue;
2362
2363     int element = TILE_GFX_ELEMENT(xx, yy);
2364     int graphic = el_act2crm(element, ACTION_DEFAULT);
2365
2366     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2367         graphic_info[graphic].anim_frames == 2)
2368       DrawLevelField(xx, yy);
2369   }
2370 }
2371
2372 static int getBorderElement(int x, int y)
2373 {
2374   int border[7][2] =
2375   {
2376     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
2377     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
2378     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
2379     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2380     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
2381     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
2382     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
2383   };
2384   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2385   int steel_position = (x == -1         && y == -1              ? 0 :
2386                         x == lev_fieldx && y == -1              ? 1 :
2387                         x == -1         && y == lev_fieldy      ? 2 :
2388                         x == lev_fieldx && y == lev_fieldy      ? 3 :
2389                         x == -1         || x == lev_fieldx      ? 4 :
2390                         y == -1         || y == lev_fieldy      ? 5 : 6);
2391
2392   return border[steel_position][steel_type];
2393 }
2394
2395 void DrawScreenElement(int x, int y, int element)
2396 {
2397   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2398   DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2399 }
2400
2401 void DrawLevelElement(int x, int y, int element)
2402 {
2403   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2404     DrawScreenElement(SCREENX(x), SCREENY(y), element);
2405 }
2406
2407 void DrawScreenField(int x, int y)
2408 {
2409   int lx = LEVELX(x), ly = LEVELY(y);
2410   int element, content;
2411
2412   if (!IN_LEV_FIELD(lx, ly))
2413   {
2414     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2415       element = EL_EMPTY;
2416     else
2417       element = getBorderElement(lx, ly);
2418
2419     DrawScreenElement(x, y, element);
2420
2421     return;
2422   }
2423
2424   element = Tile[lx][ly];
2425   content = Store[lx][ly];
2426
2427   if (IS_MOVING(lx, ly))
2428   {
2429     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2430     boolean cut_mode = NO_CUTTING;
2431
2432     if (element == EL_QUICKSAND_EMPTYING ||
2433         element == EL_QUICKSAND_FAST_EMPTYING ||
2434         element == EL_MAGIC_WALL_EMPTYING ||
2435         element == EL_BD_MAGIC_WALL_EMPTYING ||
2436         element == EL_DC_MAGIC_WALL_EMPTYING ||
2437         element == EL_AMOEBA_DROPPING)
2438       cut_mode = CUT_ABOVE;
2439     else if (element == EL_QUICKSAND_FILLING ||
2440              element == EL_QUICKSAND_FAST_FILLING ||
2441              element == EL_MAGIC_WALL_FILLING ||
2442              element == EL_BD_MAGIC_WALL_FILLING ||
2443              element == EL_DC_MAGIC_WALL_FILLING)
2444       cut_mode = CUT_BELOW;
2445
2446     if (cut_mode == CUT_ABOVE)
2447       DrawScreenElement(x, y, element);
2448     else
2449       DrawScreenElement(x, y, EL_EMPTY);
2450
2451     if (horiz_move)
2452       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2453     else if (cut_mode == NO_CUTTING)
2454       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2455     else
2456     {
2457       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2458
2459       if (cut_mode == CUT_BELOW &&
2460           IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2461         DrawLevelElement(lx, ly + 1, element);
2462     }
2463
2464     if (content == EL_ACID)
2465     {
2466       int dir = MovDir[lx][ly];
2467       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2468       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2469
2470       DrawLevelElementThruMask(newlx, newly, EL_ACID);
2471
2472       // prevent target field from being drawn again (but without masking)
2473       // (this would happen if target field is scanned after moving element)
2474       Stop[newlx][newly] = TRUE;
2475     }
2476   }
2477   else if (IS_BLOCKED(lx, ly))
2478   {
2479     int oldx, oldy;
2480     int sx, sy;
2481     int horiz_move;
2482     boolean cut_mode = NO_CUTTING;
2483     int element_old, content_old;
2484
2485     Blocked2Moving(lx, ly, &oldx, &oldy);
2486     sx = SCREENX(oldx);
2487     sy = SCREENY(oldy);
2488     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2489                   MovDir[oldx][oldy] == MV_RIGHT);
2490
2491     element_old = Tile[oldx][oldy];
2492     content_old = Store[oldx][oldy];
2493
2494     if (element_old == EL_QUICKSAND_EMPTYING ||
2495         element_old == EL_QUICKSAND_FAST_EMPTYING ||
2496         element_old == EL_MAGIC_WALL_EMPTYING ||
2497         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2498         element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2499         element_old == EL_AMOEBA_DROPPING)
2500       cut_mode = CUT_ABOVE;
2501
2502     DrawScreenElement(x, y, EL_EMPTY);
2503
2504     if (horiz_move)
2505       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2506                                NO_CUTTING);
2507     else if (cut_mode == NO_CUTTING)
2508       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2509                                cut_mode);
2510     else
2511       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2512                                cut_mode);
2513   }
2514   else if (IS_DRAWABLE(element))
2515     DrawScreenElement(x, y, element);
2516   else
2517     DrawScreenElement(x, y, EL_EMPTY);
2518 }
2519
2520 void DrawLevelField(int x, int y)
2521 {
2522   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2523     DrawScreenField(SCREENX(x), SCREENY(y));
2524   else if (IS_MOVING(x, y))
2525   {
2526     int newx,newy;
2527
2528     Moving2Blocked(x, y, &newx, &newy);
2529     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2530       DrawScreenField(SCREENX(newx), SCREENY(newy));
2531   }
2532   else if (IS_BLOCKED(x, y))
2533   {
2534     int oldx, oldy;
2535
2536     Blocked2Moving(x, y, &oldx, &oldy);
2537     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2538       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2539   }
2540 }
2541
2542 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2543                                 int (*el2img_function)(int), boolean masked,
2544                                 int element_bits_draw)
2545 {
2546   int element_base = map_mm_wall_element(element);
2547   int element_bits = (IS_DF_WALL(element) ?
2548                       element - EL_DF_WALL_START :
2549                       IS_MM_WALL(element) ?
2550                       element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2551   int graphic = el2img_function(element_base);
2552   int tilesize_draw = tilesize / 2;
2553   Bitmap *src_bitmap;
2554   int src_x, src_y;
2555   int i;
2556
2557   getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2558
2559   for (i = 0; i < 4; i++)
2560   {
2561     int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2562     int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2563
2564     if (!(element_bits_draw & (1 << i)))
2565       continue;
2566
2567     if (element_bits & (1 << i))
2568     {
2569       if (masked)
2570         BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2571                          tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2572       else
2573         BlitBitmap(src_bitmap, drawto, src_x, src_y,
2574                    tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2575     }
2576     else
2577     {
2578       if (!masked)
2579         ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2580                        tilesize_draw, tilesize_draw);
2581     }
2582   }
2583 }
2584
2585 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2586                            boolean masked, int element_bits_draw)
2587 {
2588   DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2589                       element, tilesize, el2edimg, masked, element_bits_draw);
2590 }
2591
2592 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2593                              int (*el2img_function)(int))
2594 {
2595   DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2596                       0x000f);
2597 }
2598
2599 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2600                                 boolean masked)
2601 {
2602   if (IS_MM_WALL(element))
2603   {
2604     DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2605                         element, tilesize, el2edimg, masked, 0x000f);
2606   }
2607   else
2608   {
2609     int graphic = el2edimg(element);
2610
2611     if (masked)
2612       DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2613     else
2614       DrawSizedGraphic(x, y, graphic, 0, tilesize);
2615   }
2616 }
2617
2618 void DrawSizedElement(int x, int y, int element, int tilesize)
2619 {
2620   DrawSizedElementExt(x, y, element, tilesize, FALSE);
2621 }
2622
2623 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2624 {
2625   DrawSizedElementExt(x, y, element, tilesize, TRUE);
2626 }
2627
2628 void DrawMiniElement(int x, int y, int element)
2629 {
2630   int graphic;
2631
2632   graphic = el2edimg(element);
2633   DrawMiniGraphic(x, y, graphic);
2634 }
2635
2636 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2637                             int tilesize)
2638 {
2639   int x = sx + scroll_x, y = sy + scroll_y;
2640
2641   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2642     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2643   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2644     DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2645   else
2646     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2647 }
2648
2649 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2650 {
2651   int x = sx + scroll_x, y = sy + scroll_y;
2652
2653   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2654     DrawMiniElement(sx, sy, EL_EMPTY);
2655   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2656     DrawMiniElement(sx, sy, Tile[x][y]);
2657   else
2658     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2659 }
2660
2661 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2662                                         int x, int y, int xsize, int ysize,
2663                                         int tile_width, int tile_height)
2664 {
2665   Bitmap *src_bitmap;
2666   int src_x, src_y;
2667   int dst_x = startx + x * tile_width;
2668   int dst_y = starty + y * tile_height;
2669   int width  = graphic_info[graphic].width;
2670   int height = graphic_info[graphic].height;
2671   int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
2672   int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2673   int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
2674   int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2675   int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
2676   int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2677   boolean draw_masked = graphic_info[graphic].draw_masked;
2678
2679   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2680
2681   if (src_bitmap == NULL || width < tile_width || height < tile_height)
2682   {
2683     ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2684     return;
2685   }
2686
2687   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
2688             inner_sx + (x - 1) * tile_width  % inner_width);
2689   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2690             inner_sy + (y - 1) * tile_height % inner_height);
2691
2692   if (draw_masked)
2693     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2694                      dst_x, dst_y);
2695   else
2696     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2697                dst_x, dst_y);
2698 }
2699
2700 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2701                                    int x, int y, int xsize, int ysize,
2702                                    int font_nr)
2703 {
2704   int font_width  = getFontWidth(font_nr);
2705   int font_height = getFontHeight(font_nr);
2706
2707   DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2708                               font_width, font_height);
2709 }
2710
2711 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2712 {
2713   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2714   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2715   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2716   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2717   boolean no_delay = (tape.warp_forward);
2718   unsigned int anim_delay = 0;
2719   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2720   int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2721   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2722   int font_width = getFontWidth(font_nr);
2723   int font_height = getFontHeight(font_nr);
2724   int max_xsize = level.envelope[envelope_nr].xsize;
2725   int max_ysize = level.envelope[envelope_nr].ysize;
2726   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2727   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2728   int xend = max_xsize;
2729   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2730   int xstep = (xstart < xend ? 1 : 0);
2731   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2732   int start = 0;
2733   int end = MAX(xend - xstart, yend - ystart);
2734   int i;
2735
2736   for (i = start; i <= end; i++)
2737   {
2738     int last_frame = end;       // last frame of this "for" loop
2739     int x = xstart + i * xstep;
2740     int y = ystart + i * ystep;
2741     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2742     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2743     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2744     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2745     int xx, yy;
2746
2747     SetDrawtoField(DRAW_TO_FIELDBUFFER);
2748
2749     BlitScreenToBitmap(backbuffer);
2750
2751     SetDrawtoField(DRAW_TO_BACKBUFFER);
2752
2753     for (yy = 0; yy < ysize; yy++)
2754       for (xx = 0; xx < xsize; xx++)
2755         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2756
2757     DrawTextBuffer(sx + font_width, sy + font_height,
2758                    level.envelope[envelope_nr].text, font_nr, max_xsize,
2759                    xsize - 2, ysize - 2, 0, mask_mode,
2760                    level.envelope[envelope_nr].autowrap,
2761                    level.envelope[envelope_nr].centered, FALSE);
2762
2763     redraw_mask |= REDRAW_FIELD;
2764     BackToFront();
2765
2766     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2767   }
2768
2769   ClearAutoRepeatKeyEvents();
2770 }
2771
2772 void ShowEnvelope(int envelope_nr)
2773 {
2774   int element = EL_ENVELOPE_1 + envelope_nr;
2775   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2776   int sound_opening = element_info[element].sound[ACTION_OPENING];
2777   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2778   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2779   boolean no_delay = (tape.warp_forward);
2780   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2781   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2782   int anim_mode = graphic_info[graphic].anim_mode;
2783   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2784                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2785   boolean overlay_enabled = GetOverlayEnabled();
2786
2787   game.envelope_active = TRUE;  // needed for RedrawPlayfield() events
2788
2789   SetOverlayEnabled(FALSE);
2790   UnmapAllGadgets();
2791
2792   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2793
2794   if (anim_mode == ANIM_DEFAULT)
2795     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2796
2797   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2798
2799   if (tape.playing)
2800     Delay_WithScreenUpdates(wait_delay_value);
2801   else
2802     WaitForEventToContinue();
2803
2804   RemapAllGadgets();
2805   SetOverlayEnabled(overlay_enabled);
2806
2807   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2808
2809   if (anim_mode != ANIM_NONE)
2810     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2811
2812   if (anim_mode == ANIM_DEFAULT)
2813     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2814
2815   game.envelope_active = FALSE;
2816
2817   SetDrawtoField(DRAW_TO_FIELDBUFFER);
2818
2819   redraw_mask |= REDRAW_FIELD;
2820   BackToFront();
2821 }
2822
2823 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2824                                            int xsize, int ysize)
2825 {
2826   if (!global.use_envelope_request ||
2827       request.sort_priority <= 0)
2828     return;
2829
2830   if (request.bitmap == NULL ||
2831       xsize > request.xsize ||
2832       ysize > request.ysize)
2833   {
2834     if (request.bitmap != NULL)
2835       FreeBitmap(request.bitmap);
2836
2837     request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2838
2839     SDL_Surface *surface = request.bitmap->surface;
2840
2841     if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2842       Fail("SDLGetNativeSurface() failed");
2843   }
2844
2845   BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2846
2847   SDLFreeBitmapTextures(request.bitmap);
2848   SDLCreateBitmapTextures(request.bitmap);
2849
2850   // set envelope request run-time values
2851   request.sx = sx;
2852   request.sy = sy;
2853   request.xsize = xsize;
2854   request.ysize = ysize;
2855 }
2856
2857 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
2858 {
2859   if (global.use_envelope_request &&
2860       game.request_active_or_moving &&
2861       request.sort_priority > 0 &&
2862       drawing_target == DRAW_TO_SCREEN &&
2863       drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
2864   {
2865     BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
2866                  request.sx, request.sy);
2867   }
2868 }
2869
2870 static void setRequestBasePosition(int *x, int *y)
2871 {
2872   int sx_base, sy_base;
2873
2874   if (request.x != -1)
2875     sx_base = request.x;
2876   else if (request.align == ALIGN_LEFT)
2877     sx_base = SX;
2878   else if (request.align == ALIGN_RIGHT)
2879     sx_base = SX + SXSIZE;
2880   else
2881     sx_base = SX + SXSIZE / 2;
2882
2883   if (request.y != -1)
2884     sy_base = request.y;
2885   else if (request.valign == VALIGN_TOP)
2886     sy_base = SY;
2887   else if (request.valign == VALIGN_BOTTOM)
2888     sy_base = SY + SYSIZE;
2889   else
2890     sy_base = SY + SYSIZE / 2;
2891
2892   *x = sx_base;
2893   *y = sy_base;
2894 }
2895
2896 static void setRequestPositionExt(int *x, int *y, int width, int height,
2897                                   boolean add_border_size)
2898 {
2899   int border_size = request.border_size;
2900   int sx_base, sy_base;
2901   int sx, sy;
2902
2903   setRequestBasePosition(&sx_base, &sy_base);
2904
2905   if (request.align == ALIGN_LEFT)
2906     sx = sx_base;
2907   else if (request.align == ALIGN_RIGHT)
2908     sx = sx_base - width;
2909   else
2910     sx = sx_base - width  / 2;
2911
2912   if (request.valign == VALIGN_TOP)
2913     sy = sy_base;
2914   else if (request.valign == VALIGN_BOTTOM)
2915     sy = sy_base - height;
2916   else
2917     sy = sy_base - height / 2;
2918
2919   sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2920   sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2921
2922   if (add_border_size)
2923   {
2924     sx += border_size;
2925     sy += border_size;
2926   }
2927
2928   *x = sx;
2929   *y = sy;
2930 }
2931
2932 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2933 {
2934   setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2935 }
2936
2937 static void DrawEnvelopeRequest(char *text)
2938 {
2939   char *text_final = text;
2940   char *text_door_style = NULL;
2941   int graphic = IMG_BACKGROUND_REQUEST;
2942   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2943   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2944   int font_nr = FONT_REQUEST;
2945   int font_width = getFontWidth(font_nr);
2946   int font_height = getFontHeight(font_nr);
2947   int border_size = request.border_size;
2948   int line_spacing = request.line_spacing;
2949   int line_height = font_height + line_spacing;
2950   int max_text_width  = request.width  - 2 * border_size;
2951   int max_text_height = request.height - 2 * border_size;
2952   int line_length = max_text_width  / font_width;
2953   int max_lines   = max_text_height / line_height;
2954   int text_width = line_length * font_width;
2955   int width = request.width;
2956   int height = request.height;
2957   int tile_size = MAX(request.step_offset, 1);
2958   int x_steps = width  / tile_size;
2959   int y_steps = height / tile_size;
2960   int sx_offset = border_size;
2961   int sy_offset = border_size;
2962   int sx, sy;
2963   int i, x, y;
2964
2965   if (request.centered)
2966     sx_offset = (request.width - text_width) / 2;
2967
2968   if (request.wrap_single_words && !request.autowrap)
2969   {
2970     char *src_text_ptr, *dst_text_ptr;
2971
2972     text_door_style = checked_malloc(2 * strlen(text) + 1);
2973
2974     src_text_ptr = text;
2975     dst_text_ptr = text_door_style;
2976
2977     while (*src_text_ptr)
2978     {
2979       if (*src_text_ptr == ' ' ||
2980           *src_text_ptr == '?' ||
2981           *src_text_ptr == '!')
2982         *dst_text_ptr++ = '\n';
2983
2984       if (*src_text_ptr != ' ')
2985         *dst_text_ptr++ = *src_text_ptr;
2986
2987       src_text_ptr++;
2988     }
2989
2990     *dst_text_ptr = '\0';
2991
2992     text_final = text_door_style;
2993   }
2994
2995   setRequestPosition(&sx, &sy, FALSE);
2996
2997   ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2998
2999   for (y = 0; y < y_steps; y++)
3000     for (x = 0; x < x_steps; x++)
3001       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3002                                   x, y, x_steps, y_steps,
3003                                   tile_size, tile_size);
3004
3005   // force DOOR font inside door area
3006   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3007
3008   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3009                  line_length, -1, max_lines, line_spacing, mask_mode,
3010                  request.autowrap, request.centered, FALSE);
3011
3012   ResetFontStatus();
3013
3014   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3015     RedrawGadget(tool_gadget[i]);
3016
3017   // store readily prepared envelope request for later use when animating
3018   BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3019
3020   PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3021
3022   if (text_door_style)
3023     free(text_door_style);
3024 }
3025
3026 static void AnimateEnvelopeRequest(int anim_mode, int action)
3027 {
3028   int graphic = IMG_BACKGROUND_REQUEST;
3029   boolean draw_masked = graphic_info[graphic].draw_masked;
3030   int delay_value_normal = request.step_delay;
3031   int delay_value_fast = delay_value_normal / 2;
3032   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3033   boolean no_delay = (tape.warp_forward);
3034   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3035   int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3036   unsigned int anim_delay = 0;
3037
3038   int tile_size = MAX(request.step_offset, 1);
3039   int max_xsize = request.width  / tile_size;
3040   int max_ysize = request.height / tile_size;
3041   int max_xsize_inner = max_xsize - 2;
3042   int max_ysize_inner = max_ysize - 2;
3043
3044   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3045   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3046   int xend = max_xsize_inner;
3047   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3048   int xstep = (xstart < xend ? 1 : 0);
3049   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3050   int start = 0;
3051   int end = MAX(xend - xstart, yend - ystart);
3052   int i;
3053
3054   if (setup.quick_doors)
3055   {
3056     xstart = xend;
3057     ystart = yend;
3058     end = 0;
3059   }
3060
3061   for (i = start; i <= end; i++)
3062   {
3063     int last_frame = end;       // last frame of this "for" loop
3064     int x = xstart + i * xstep;
3065     int y = ystart + i * ystep;
3066     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3067     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3068     int xsize_size_left = (xsize - 1) * tile_size;
3069     int ysize_size_top  = (ysize - 1) * tile_size;
3070     int max_xsize_pos = (max_xsize - 1) * tile_size;
3071     int max_ysize_pos = (max_ysize - 1) * tile_size;
3072     int width  = xsize * tile_size;
3073     int height = ysize * tile_size;
3074     int src_x, src_y;
3075     int dst_x, dst_y;
3076     int xx, yy;
3077
3078     setRequestPosition(&src_x, &src_y, FALSE);
3079     setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3080
3081     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3082
3083     for (yy = 0; yy < 2; yy++)
3084     {
3085       for (xx = 0; xx < 2; xx++)
3086       {
3087         int src_xx = src_x + xx * max_xsize_pos;
3088         int src_yy = src_y + yy * max_ysize_pos;
3089         int dst_xx = dst_x + xx * xsize_size_left;
3090         int dst_yy = dst_y + yy * ysize_size_top;
3091         int xx_size = (xx ? tile_size : xsize_size_left);
3092         int yy_size = (yy ? tile_size : ysize_size_top);
3093
3094         if (draw_masked)
3095           BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3096                            src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3097         else
3098           BlitBitmap(bitmap_db_store_2, backbuffer,
3099                      src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3100       }
3101     }
3102
3103     PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3104
3105     redraw_mask |= REDRAW_FIELD;
3106
3107     BackToFront();
3108
3109     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3110   }
3111
3112   ClearAutoRepeatKeyEvents();
3113 }
3114
3115 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3116 {
3117   int graphic = IMG_BACKGROUND_REQUEST;
3118   int sound_opening = SND_REQUEST_OPENING;
3119   int sound_closing = SND_REQUEST_CLOSING;
3120   int anim_mode_1 = request.anim_mode;                  // (higher priority)
3121   int anim_mode_2 = graphic_info[graphic].anim_mode;    // (lower priority)
3122   int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3123   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3124                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3125
3126   if (game_status == GAME_MODE_PLAYING)
3127     BlitScreenToBitmap(backbuffer);
3128
3129   SetDrawtoField(DRAW_TO_BACKBUFFER);
3130
3131   // SetDrawBackgroundMask(REDRAW_NONE);
3132
3133   if (action == ACTION_OPENING)
3134   {
3135     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3136
3137     if (req_state & REQ_ASK)
3138     {
3139       MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3140       MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3141       MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3142       MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3143     }
3144     else if (req_state & REQ_CONFIRM)
3145     {
3146       MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3147       MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3148     }
3149     else if (req_state & REQ_PLAYER)
3150     {
3151       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3152       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3153       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3154       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3155     }
3156
3157     DrawEnvelopeRequest(text);
3158   }
3159
3160   game.envelope_active = TRUE;  // needed for RedrawPlayfield() events
3161
3162   if (action == ACTION_OPENING)
3163   {
3164     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3165
3166     if (anim_mode == ANIM_DEFAULT)
3167       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3168
3169     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3170   }
3171   else
3172   {
3173     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3174
3175     if (anim_mode != ANIM_NONE)
3176       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3177
3178     if (anim_mode == ANIM_DEFAULT)
3179       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3180   }
3181
3182   game.envelope_active = FALSE;
3183
3184   if (action == ACTION_CLOSING)
3185     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3186
3187   // SetDrawBackgroundMask(last_draw_background_mask);
3188
3189   redraw_mask |= REDRAW_FIELD;
3190
3191   BackToFront();
3192
3193   if (action == ACTION_CLOSING &&
3194       game_status == GAME_MODE_PLAYING &&
3195       level.game_engine_type == GAME_ENGINE_TYPE_RND)
3196     SetDrawtoField(DRAW_TO_FIELDBUFFER);
3197 }
3198
3199 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3200 {
3201   if (IS_MM_WALL(element))
3202   {
3203     DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3204   }
3205   else
3206   {
3207     Bitmap *src_bitmap;
3208     int src_x, src_y;
3209     int graphic = el2preimg(element);
3210
3211     getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3212     BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3213                dst_x, dst_y);
3214   }
3215 }
3216
3217 void DrawLevel(int draw_background_mask)
3218 {
3219   int x,y;
3220
3221   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3222   SetDrawBackgroundMask(draw_background_mask);
3223
3224   ClearField();
3225
3226   for (x = BX1; x <= BX2; x++)
3227     for (y = BY1; y <= BY2; y++)
3228       DrawScreenField(x, y);
3229
3230   redraw_mask |= REDRAW_FIELD;
3231 }
3232
3233 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3234                     int tilesize)
3235 {
3236   int x,y;
3237
3238   for (x = 0; x < size_x; x++)
3239     for (y = 0; y < size_y; y++)
3240       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3241
3242   redraw_mask |= REDRAW_FIELD;
3243 }
3244
3245 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3246 {
3247   int x,y;
3248
3249   for (x = 0; x < size_x; x++)
3250     for (y = 0; y < size_y; y++)
3251       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3252
3253   redraw_mask |= REDRAW_FIELD;
3254 }
3255
3256 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3257 {
3258   boolean show_level_border = (BorderElement != EL_EMPTY);
3259   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3260   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3261   int tile_size = preview.tile_size;
3262   int preview_width  = preview.xsize * tile_size;
3263   int preview_height = preview.ysize * tile_size;
3264   int real_preview_xsize = MIN(level_xsize, preview.xsize);
3265   int real_preview_ysize = MIN(level_ysize, preview.ysize);
3266   int real_preview_width  = real_preview_xsize * tile_size;
3267   int real_preview_height = real_preview_ysize * tile_size;
3268   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3269   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3270   int x, y;
3271
3272   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3273     return;
3274
3275   DrawBackground(dst_x, dst_y, preview_width, preview_height);
3276
3277   dst_x += (preview_width  - real_preview_width)  / 2;
3278   dst_y += (preview_height - real_preview_height) / 2;
3279
3280   for (x = 0; x < real_preview_xsize; x++)
3281   {
3282     for (y = 0; y < real_preview_ysize; y++)
3283     {
3284       int lx = from_x + x + (show_level_border ? -1 : 0);
3285       int ly = from_y + y + (show_level_border ? -1 : 0);
3286       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3287                      getBorderElement(lx, ly));
3288
3289       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3290                          element, tile_size);
3291     }
3292   }
3293
3294   redraw_mask |= REDRAW_FIELD;
3295 }
3296
3297 #define MICROLABEL_EMPTY                0
3298 #define MICROLABEL_LEVEL_NAME           1
3299 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
3300 #define MICROLABEL_LEVEL_AUTHOR         3
3301 #define MICROLABEL_IMPORTED_FROM_HEAD   4
3302 #define MICROLABEL_IMPORTED_FROM        5
3303 #define MICROLABEL_IMPORTED_BY_HEAD     6
3304 #define MICROLABEL_IMPORTED_BY          7
3305
3306 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3307 {
3308   int max_text_width = SXSIZE;
3309   int font_width = getFontWidth(font_nr);
3310
3311   if (pos->align == ALIGN_CENTER)
3312     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3313   else if (pos->align == ALIGN_RIGHT)
3314     max_text_width = pos->x;
3315   else
3316     max_text_width = SXSIZE - pos->x;
3317
3318   return max_text_width / font_width;
3319 }
3320
3321 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3322 {
3323   char label_text[MAX_OUTPUT_LINESIZE + 1];
3324   int max_len_label_text;
3325   int font_nr = pos->font;
3326   int i;
3327
3328   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3329     return;
3330
3331   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3332       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3333       mode == MICROLABEL_IMPORTED_BY_HEAD)
3334     font_nr = pos->font_alt;
3335
3336   max_len_label_text = getMaxTextLength(pos, font_nr);
3337
3338   if (pos->size != -1)
3339     max_len_label_text = pos->size;
3340
3341   for (i = 0; i < max_len_label_text; i++)
3342     label_text[i] = ' ';
3343   label_text[max_len_label_text] = '\0';
3344
3345   if (strlen(label_text) > 0)
3346     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3347
3348   strncpy(label_text,
3349           (mode == MICROLABEL_LEVEL_NAME ? level.name :
3350            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3351            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3352            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3353            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3354            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3355            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3356           max_len_label_text);
3357   label_text[max_len_label_text] = '\0';
3358
3359   if (strlen(label_text) > 0)
3360     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3361
3362   redraw_mask |= REDRAW_FIELD;
3363 }
3364
3365 static void DrawPreviewLevelLabel(int mode)
3366 {
3367   DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3368 }
3369
3370 static void DrawPreviewLevelInfo(int mode)
3371 {
3372   if (mode == MICROLABEL_LEVEL_NAME)
3373     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3374   else if (mode == MICROLABEL_LEVEL_AUTHOR)
3375     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3376 }
3377
3378 static void DrawPreviewLevelExt(boolean restart)
3379 {
3380   static unsigned int scroll_delay = 0;
3381   static unsigned int label_delay = 0;
3382   static int from_x, from_y, scroll_direction;
3383   static int label_state, label_counter;
3384   unsigned int scroll_delay_value = preview.step_delay;
3385   boolean show_level_border = (BorderElement != EL_EMPTY);
3386   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3387   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3388
3389   if (restart)
3390   {
3391     from_x = 0;
3392     from_y = 0;
3393
3394     if (preview.anim_mode == ANIM_CENTERED)
3395     {
3396       if (level_xsize > preview.xsize)
3397         from_x = (level_xsize - preview.xsize) / 2;
3398       if (level_ysize > preview.ysize)
3399         from_y = (level_ysize - preview.ysize) / 2;
3400     }
3401
3402     from_x += preview.xoffset;
3403     from_y += preview.yoffset;
3404
3405     scroll_direction = MV_RIGHT;
3406     label_state = 1;
3407     label_counter = 0;
3408
3409     DrawPreviewLevelPlayfield(from_x, from_y);
3410     DrawPreviewLevelLabel(label_state);
3411
3412     DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3413     DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3414
3415     // initialize delay counters
3416     DelayReached(&scroll_delay, 0);
3417     DelayReached(&label_delay, 0);
3418
3419     if (leveldir_current->name)
3420     {
3421       struct TextPosInfo *pos = &menu.main.text.level_info_1;
3422       char label_text[MAX_OUTPUT_LINESIZE + 1];
3423       int font_nr = pos->font;
3424       int max_len_label_text = getMaxTextLength(pos, font_nr);
3425
3426       if (pos->size != -1)
3427         max_len_label_text = pos->size;
3428
3429       strncpy(label_text, leveldir_current->name, max_len_label_text);
3430       label_text[max_len_label_text] = '\0';
3431
3432       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3433         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3434     }
3435
3436     return;
3437   }
3438
3439   // scroll preview level, if needed
3440   if (preview.anim_mode != ANIM_NONE &&
3441       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3442       DelayReached(&scroll_delay, scroll_delay_value))
3443   {
3444     switch (scroll_direction)
3445     {
3446       case MV_LEFT:
3447         if (from_x > 0)
3448         {
3449           from_x -= preview.step_offset;
3450           from_x = (from_x < 0 ? 0 : from_x);
3451         }
3452         else
3453           scroll_direction = MV_UP;
3454         break;
3455
3456       case MV_RIGHT:
3457         if (from_x < level_xsize - preview.xsize)
3458         {
3459           from_x += preview.step_offset;
3460           from_x = (from_x > level_xsize - preview.xsize ?
3461                     level_xsize - preview.xsize : from_x);
3462         }
3463         else
3464           scroll_direction = MV_DOWN;
3465         break;
3466
3467       case MV_UP:
3468         if (from_y > 0)
3469         {
3470           from_y -= preview.step_offset;
3471           from_y = (from_y < 0 ? 0 : from_y);
3472         }
3473         else
3474           scroll_direction = MV_RIGHT;
3475         break;
3476
3477       case MV_DOWN:
3478         if (from_y < level_ysize - preview.ysize)
3479         {
3480           from_y += preview.step_offset;
3481           from_y = (from_y > level_ysize - preview.ysize ?
3482                     level_ysize - preview.ysize : from_y);
3483         }
3484         else
3485           scroll_direction = MV_LEFT;
3486         break;
3487
3488       default:
3489         break;
3490     }
3491
3492     DrawPreviewLevelPlayfield(from_x, from_y);
3493   }
3494
3495   // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3496   // redraw micro level label, if needed
3497   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3498       !strEqual(level.author, ANONYMOUS_NAME) &&
3499       !strEqual(level.author, leveldir_current->name) &&
3500       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3501   {
3502     int max_label_counter = 23;
3503
3504     if (leveldir_current->imported_from != NULL &&
3505         strlen(leveldir_current->imported_from) > 0)
3506       max_label_counter += 14;
3507     if (leveldir_current->imported_by != NULL &&
3508         strlen(leveldir_current->imported_by) > 0)
3509       max_label_counter += 14;
3510
3511     label_counter = (label_counter + 1) % max_label_counter;
3512     label_state = (label_counter >= 0 && label_counter <= 7 ?
3513                    MICROLABEL_LEVEL_NAME :
3514                    label_counter >= 9 && label_counter <= 12 ?
3515                    MICROLABEL_LEVEL_AUTHOR_HEAD :
3516                    label_counter >= 14 && label_counter <= 21 ?
3517                    MICROLABEL_LEVEL_AUTHOR :
3518                    label_counter >= 23 && label_counter <= 26 ?
3519                    MICROLABEL_IMPORTED_FROM_HEAD :
3520                    label_counter >= 28 && label_counter <= 35 ?
3521                    MICROLABEL_IMPORTED_FROM :
3522                    label_counter >= 37 && label_counter <= 40 ?
3523                    MICROLABEL_IMPORTED_BY_HEAD :
3524                    label_counter >= 42 && label_counter <= 49 ?
3525                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3526
3527     if (leveldir_current->imported_from == NULL &&
3528         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3529          label_state == MICROLABEL_IMPORTED_FROM))
3530       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3531                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3532
3533     DrawPreviewLevelLabel(label_state);
3534   }
3535 }
3536
3537 void DrawPreviewPlayers(void)
3538 {
3539   if (game_status != GAME_MODE_MAIN)
3540     return;
3541
3542   // do not draw preview players if level preview redefined, but players aren't
3543   if (preview.redefined && !menu.main.preview_players.redefined)
3544     return;
3545
3546   boolean player_found[MAX_PLAYERS];
3547   int num_players = 0;
3548   int i, x, y;
3549
3550   for (i = 0; i < MAX_PLAYERS; i++)
3551     player_found[i] = FALSE;
3552
3553   // check which players can be found in the level (simple approach)
3554   for (x = 0; x < lev_fieldx; x++)
3555   {
3556     for (y = 0; y < lev_fieldy; y++)
3557     {
3558       int element = level.field[x][y];
3559
3560       if (ELEM_IS_PLAYER(element))
3561       {
3562         int player_nr = GET_PLAYER_NR(element);
3563
3564         player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3565
3566         if (!player_found[player_nr])
3567           num_players++;
3568
3569         player_found[player_nr] = TRUE;
3570       }
3571     }
3572   }
3573
3574   struct TextPosInfo *pos = &menu.main.preview_players;
3575   int tile_size = pos->tile_size;
3576   int border_size = pos->border_size;
3577   int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3578   int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3579   int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3580   int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3581   int max_players_width  = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3582   int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3583   int all_players_width  = (num_players - 1) * player_xoffset + tile_size;
3584   int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3585   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3586   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3587   int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width,  pos->align);
3588   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3589
3590   // clear area in which the players will be drawn
3591   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3592                              max_players_width, max_players_height);
3593
3594   if (!network.enabled && !setup.team_mode)
3595     return;
3596
3597   // only draw players if level is suited for team mode
3598   if (num_players < 2)
3599     return;
3600
3601   // draw all players that were found in the level
3602   for (i = 0; i < MAX_PLAYERS; i++)
3603   {
3604     if (player_found[i])
3605     {
3606       int graphic = el2img(EL_PLAYER_1 + i);
3607
3608       DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3609
3610       xpos += player_xoffset;
3611       ypos += player_yoffset;
3612     }
3613   }
3614 }
3615
3616 void DrawPreviewLevelInitial(void)
3617 {
3618   DrawPreviewLevelExt(TRUE);
3619   DrawPreviewPlayers();
3620 }
3621
3622 void DrawPreviewLevelAnimation(void)
3623 {
3624   DrawPreviewLevelExt(FALSE);
3625 }
3626
3627 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3628                               int border_size, int font_nr)
3629 {
3630   int graphic = el2img(EL_PLAYER_1 + player_nr);
3631   int font_height = getFontHeight(font_nr);
3632   int player_height = MAX(tile_size, font_height);
3633   int xoffset_text = tile_size + border_size;
3634   int yoffset_text    = (player_height - font_height) / 2;
3635   int yoffset_graphic = (player_height - tile_size) / 2;
3636   char *player_name = getNetworkPlayerName(player_nr + 1);
3637
3638   DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3639                               tile_size);
3640   DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3641 }
3642
3643 static void DrawNetworkPlayersExt(boolean force)
3644 {
3645   if (game_status != GAME_MODE_MAIN)
3646     return;
3647
3648   if (!network.connected && !force)
3649     return;
3650
3651   // do not draw network players if level preview redefined, but players aren't
3652   if (preview.redefined && !menu.main.network_players.redefined)
3653     return;
3654
3655   int num_players = 0;
3656   int i;
3657
3658   for (i = 0; i < MAX_PLAYERS; i++)
3659     if (stored_player[i].connected_network)
3660       num_players++;
3661
3662   struct TextPosInfo *pos = &menu.main.network_players;
3663   int tile_size = pos->tile_size;
3664   int border_size = pos->border_size;
3665   int xoffset_text = tile_size + border_size;
3666   int font_nr = pos->font;
3667   int font_width = getFontWidth(font_nr);
3668   int font_height = getFontHeight(font_nr);
3669   int player_height = MAX(tile_size, font_height);
3670   int player_yoffset = player_height + border_size;
3671   int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3672   int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3673   int all_players_height = num_players * player_yoffset - border_size;
3674   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3675   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3676   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3677
3678   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3679                              max_players_width, max_players_height);
3680
3681   // first draw local network player ...
3682   for (i = 0; i < MAX_PLAYERS; i++)
3683   {
3684     if (stored_player[i].connected_network &&
3685         stored_player[i].connected_locally)
3686     {
3687       char *player_name = getNetworkPlayerName(i + 1);
3688       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3689       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3690
3691       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3692
3693       ypos += player_yoffset;
3694     }
3695   }
3696
3697   // ... then draw all other network players
3698   for (i = 0; i < MAX_PLAYERS; i++)
3699   {
3700     if (stored_player[i].connected_network &&
3701         !stored_player[i].connected_locally)
3702     {
3703       char *player_name = getNetworkPlayerName(i + 1);
3704       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3705       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3706
3707       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3708
3709       ypos += player_yoffset;
3710     }
3711   }
3712 }
3713
3714 void DrawNetworkPlayers(void)
3715 {
3716   DrawNetworkPlayersExt(FALSE);
3717 }
3718
3719 void ClearNetworkPlayers(void)
3720 {
3721   DrawNetworkPlayersExt(TRUE);
3722 }
3723
3724 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3725                                     int graphic, int sync_frame,
3726                                     int mask_mode)
3727 {
3728   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3729
3730   if (mask_mode == USE_MASKING)
3731     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3732   else
3733     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3734 }
3735
3736 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3737                                   int graphic, int sync_frame, int mask_mode)
3738 {
3739   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3740
3741   if (mask_mode == USE_MASKING)
3742     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3743   else
3744     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3745 }
3746
3747 static void DrawGraphicAnimation(int x, int y, int graphic)
3748 {
3749   int lx = LEVELX(x), ly = LEVELY(y);
3750
3751   if (!IN_SCR_FIELD(x, y))
3752     return;
3753
3754   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3755                           graphic, GfxFrame[lx][ly], NO_MASKING);
3756
3757   MarkTileDirty(x, y);
3758 }
3759
3760 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3761 {
3762   int lx = LEVELX(x), ly = LEVELY(y);
3763
3764   if (!IN_SCR_FIELD(x, y))
3765     return;
3766
3767   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3768                           graphic, GfxFrame[lx][ly], NO_MASKING);
3769   MarkTileDirty(x, y);
3770 }
3771
3772 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3773 {
3774   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3775 }
3776
3777 void DrawLevelElementAnimation(int x, int y, int element)
3778 {
3779   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3780
3781   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3782 }
3783
3784 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3785 {
3786   int sx = SCREENX(x), sy = SCREENY(y);
3787
3788   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3789     return;
3790
3791   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3792     return;
3793
3794   DrawGraphicAnimation(sx, sy, graphic);
3795
3796 #if 1
3797   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3798     DrawLevelFieldCrumbled(x, y);
3799 #else
3800   if (GFX_CRUMBLED(Tile[x][y]))
3801     DrawLevelFieldCrumbled(x, y);
3802 #endif
3803 }
3804
3805 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3806 {
3807   int sx = SCREENX(x), sy = SCREENY(y);
3808   int graphic;
3809
3810   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3811     return;
3812
3813   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3814
3815   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3816     return;
3817
3818   DrawGraphicAnimation(sx, sy, graphic);
3819
3820   if (GFX_CRUMBLED(element))
3821     DrawLevelFieldCrumbled(x, y);
3822 }
3823
3824 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3825 {
3826   if (player->use_murphy)
3827   {
3828     // this works only because currently only one player can be "murphy" ...
3829     static int last_horizontal_dir = MV_LEFT;
3830     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3831
3832     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3833       last_horizontal_dir = move_dir;
3834
3835     if (graphic == IMG_SP_MURPHY)       // undefined => use special graphic
3836     {
3837       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3838
3839       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3840     }
3841
3842     return graphic;
3843   }
3844   else
3845     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3846 }
3847
3848 static boolean equalGraphics(int graphic1, int graphic2)
3849 {
3850   struct GraphicInfo *g1 = &graphic_info[graphic1];
3851   struct GraphicInfo *g2 = &graphic_info[graphic2];
3852
3853   return (g1->bitmap      == g2->bitmap &&
3854           g1->src_x       == g2->src_x &&
3855           g1->src_y       == g2->src_y &&
3856           g1->anim_frames == g2->anim_frames &&
3857           g1->anim_delay  == g2->anim_delay &&
3858           g1->anim_mode   == g2->anim_mode);
3859 }
3860
3861 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3862
3863 enum
3864 {
3865   DRAW_PLAYER_STAGE_INIT = 0,
3866   DRAW_PLAYER_STAGE_LAST_FIELD,
3867   DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3868 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3869   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3870   DRAW_PLAYER_STAGE_PLAYER,
3871 #else
3872   DRAW_PLAYER_STAGE_PLAYER,
3873   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3874 #endif
3875   DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3876   DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3877
3878   NUM_DRAW_PLAYER_STAGES
3879 };
3880
3881 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3882 {
3883   static int static_last_player_graphic[MAX_PLAYERS];
3884   static int static_last_player_frame[MAX_PLAYERS];
3885   static boolean static_player_is_opaque[MAX_PLAYERS];
3886   static boolean draw_player[MAX_PLAYERS];
3887   int pnr = player->index_nr;
3888
3889   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3890   {
3891     static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3892     static_last_player_frame[pnr] = player->Frame;
3893     static_player_is_opaque[pnr] = FALSE;
3894
3895     draw_player[pnr] = TRUE;
3896   }
3897
3898   if (!draw_player[pnr])
3899     return;
3900
3901 #if DEBUG
3902   if (!IN_LEV_FIELD(player->jx, player->jy))
3903   {
3904     Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
3905     Debug("draw:DrawPlayerExt", "This should never happen!");
3906
3907     draw_player[pnr] = FALSE;
3908
3909     return;
3910   }
3911 #endif
3912
3913   int last_player_graphic  = static_last_player_graphic[pnr];
3914   int last_player_frame    = static_last_player_frame[pnr];
3915   boolean player_is_opaque = static_player_is_opaque[pnr];
3916
3917   int jx = player->jx;
3918   int jy = player->jy;
3919   int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3920   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3921   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
3922   int last_jx = (player->is_moving ? jx - dx : jx);
3923   int last_jy = (player->is_moving ? jy - dy : jy);
3924   int next_jx = jx + dx;
3925   int next_jy = jy + dy;
3926   boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3927   int sx = SCREENX(jx);
3928   int sy = SCREENY(jy);
3929   int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3930   int syy = (move_dir == MV_UP   || move_dir == MV_DOWN  ? player->GfxPos : 0);
3931   int element = Tile[jx][jy];
3932   int last_element = Tile[last_jx][last_jy];
3933   int action = (player->is_pushing    ? ACTION_PUSHING         :
3934                 player->is_digging    ? ACTION_DIGGING         :
3935                 player->is_collecting ? ACTION_COLLECTING      :
3936                 player->is_moving     ? ACTION_MOVING          :
3937                 player->is_snapping   ? ACTION_SNAPPING        :
3938                 player->is_dropping   ? ACTION_DROPPING        :
3939                 player->is_waiting    ? player->action_waiting :
3940                 ACTION_DEFAULT);
3941
3942   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3943   {
3944     // ------------------------------------------------------------------------
3945     // initialize drawing the player
3946     // ------------------------------------------------------------------------
3947
3948     draw_player[pnr] = FALSE;
3949
3950     // GfxElement[][] is set to the element the player is digging or collecting;
3951     // remove also for off-screen player if the player is not moving anymore
3952     if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3953       GfxElement[jx][jy] = EL_UNDEFINED;
3954
3955     if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3956       return;
3957
3958     if (element == EL_EXPLOSION)
3959       return;
3960
3961     InitPlayerGfxAnimation(player, action, move_dir);
3962
3963     draw_player[pnr] = TRUE;
3964   }
3965   else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)