changed flood fill function to prevent stack overflow crashes
[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 start_x, int start_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   static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1425   static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1426   int old_element = field[start_x][start_y];
1427   int stack_pos = 0;
1428
1429   // do nothing if start field already has the desired content
1430   if (old_element == fill_element)
1431     return;
1432
1433   stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1434
1435   while (stack_pos > 0)
1436   {
1437     struct XY current = stack_buffer[--stack_pos];
1438     int i;
1439
1440     field[current.x][current.y] = fill_element;
1441
1442     for (i = 0; i < 4; i++)
1443     {
1444       int x = current.x + check[i].x;
1445       int y = current.y + check[i].y;
1446
1447       // check for stack buffer overflow (should not happen)
1448       if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1449         Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1450
1451       if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1452         stack_buffer[stack_pos++] = (struct XY){ x, y };
1453     }
1454   }
1455 }
1456
1457 void FloodFillLevel(int from_x, int from_y, int fill_element,
1458                     short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1459                     int max_fieldx, int max_fieldy)
1460 {
1461   FloodFillLevelExt(from_x, from_y, fill_element,
1462                     MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1463                     max_fieldx, max_fieldy);
1464 }
1465
1466 void SetRandomAnimationValue(int x, int y)
1467 {
1468   gfx.anim_random_frame = GfxRandom[x][y];
1469 }
1470
1471 int getGraphicAnimationFrame(int graphic, int sync_frame)
1472 {
1473   // animation synchronized with global frame counter, not move position
1474   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1475     sync_frame = FrameCounter;
1476
1477   return getAnimationFrame(graphic_info[graphic].anim_frames,
1478                            graphic_info[graphic].anim_delay,
1479                            graphic_info[graphic].anim_mode,
1480                            graphic_info[graphic].anim_start_frame,
1481                            sync_frame);
1482 }
1483
1484 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1485 {
1486   struct GraphicInfo *g = &graphic_info[graphic];
1487   int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1488
1489   if (tilesize == gfx.standard_tile_size)
1490     *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1491   else if (tilesize == game.tile_size)
1492     *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1493   else
1494     *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1495 }
1496
1497 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1498                         boolean get_backside)
1499 {
1500   struct GraphicInfo *g = &graphic_info[graphic];
1501   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1502   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1503
1504   if (g->offset_y == 0)         // frames are ordered horizontally
1505   {
1506     int max_width = g->anim_frames_per_line * g->width;
1507     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1508
1509     *x = pos % max_width;
1510     *y = src_y % g->height + pos / max_width * g->height;
1511   }
1512   else if (g->offset_x == 0)    // frames are ordered vertically
1513   {
1514     int max_height = g->anim_frames_per_line * g->height;
1515     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1516
1517     *x = src_x % g->width + pos / max_height * g->width;
1518     *y = pos % max_height;
1519   }
1520   else                          // frames are ordered diagonally
1521   {
1522     *x = src_x + frame * g->offset_x;
1523     *y = src_y + frame * g->offset_y;
1524   }
1525 }
1526
1527 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1528                               Bitmap **bitmap, int *x, int *y,
1529                               boolean get_backside)
1530 {
1531   struct GraphicInfo *g = &graphic_info[graphic];
1532
1533   // if no graphics defined at all, use fallback graphics
1534   if (g->bitmaps == NULL)
1535     *g = graphic_info[IMG_CHAR_EXCLAM];
1536
1537   // if no in-game graphics defined, always use standard graphic size
1538   if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1539     tilesize = TILESIZE;
1540
1541   getGraphicSourceBitmap(graphic, tilesize, bitmap);
1542   getGraphicSourceXY(graphic, frame, x, y, get_backside);
1543
1544   *x = *x * tilesize / g->tile_size;
1545   *y = *y * tilesize / g->tile_size;
1546 }
1547
1548 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1549                            Bitmap **bitmap, int *x, int *y)
1550 {
1551   getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1552 }
1553
1554 void getFixedGraphicSource(int graphic, int frame,
1555                            Bitmap **bitmap, int *x, int *y)
1556 {
1557   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1558 }
1559
1560 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1561 {
1562   getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1563 }
1564
1565 void getGlobalAnimGraphicSource(int graphic, int frame,
1566                                 Bitmap **bitmap, int *x, int *y)
1567 {
1568   struct GraphicInfo *g = &graphic_info[graphic];
1569
1570   // if no graphics defined at all, use fallback graphics
1571   if (g->bitmaps == NULL)
1572     *g = graphic_info[IMG_CHAR_EXCLAM];
1573
1574   // use original size graphics, if existing, else use standard size graphics
1575   if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1576     *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1577   else
1578     *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1579
1580   getGraphicSourceXY(graphic, frame, x, y, FALSE);
1581 }
1582
1583 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1584                                 int *x, int *y, boolean get_backside)
1585 {
1586   getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1587                            get_backside);
1588 }
1589
1590 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1591 {
1592   getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1593 }
1594
1595 void DrawGraphic(int x, int y, int graphic, int frame)
1596 {
1597 #if DEBUG
1598   if (!IN_SCR_FIELD(x, y))
1599   {
1600     Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1601     Debug("draw:DrawGraphic", "This should never happen!");
1602
1603     return;
1604   }
1605 #endif
1606
1607   DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1608                  frame);
1609
1610   MarkTileDirty(x, y);
1611 }
1612
1613 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1614 {
1615 #if DEBUG
1616   if (!IN_SCR_FIELD(x, y))
1617   {
1618     Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1619           x, y, graphic);
1620     Debug("draw:DrawFixedGraphic", "This should never happen!");
1621
1622     return;
1623   }
1624 #endif
1625
1626   DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1627                       frame);
1628   MarkTileDirty(x, y);
1629 }
1630
1631 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1632                     int frame)
1633 {
1634   Bitmap *src_bitmap;
1635   int src_x, src_y;
1636
1637   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1638
1639   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1640 }
1641
1642 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1643                          int frame)
1644 {
1645   Bitmap *src_bitmap;
1646   int src_x, src_y;
1647
1648   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1649   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1650 }
1651
1652 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1653 {
1654 #if DEBUG
1655   if (!IN_SCR_FIELD(x, y))
1656   {
1657     Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1658           x, y, graphic);
1659     Debug("draw:DrawGraphicThruMask", "This should never happen!");
1660
1661     return;
1662   }
1663 #endif
1664
1665   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1666                          graphic, frame);
1667
1668   MarkTileDirty(x, y);
1669 }
1670
1671 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1672 {
1673 #if DEBUG
1674   if (!IN_SCR_FIELD(x, y))
1675   {
1676     Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1677           x, y, graphic);
1678     Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1679
1680     return;
1681   }
1682 #endif
1683
1684   DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1685                               graphic, frame);
1686   MarkTileDirty(x, y);
1687 }
1688
1689 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1690                             int frame)
1691 {
1692   Bitmap *src_bitmap;
1693   int src_x, src_y;
1694
1695   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1696
1697   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1698                    dst_x, dst_y);
1699 }
1700
1701 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1702                                  int graphic, int frame)
1703 {
1704   Bitmap *src_bitmap;
1705   int src_x, src_y;
1706
1707   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1708
1709   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1710                    dst_x, dst_y);
1711 }
1712
1713 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1714 {
1715   DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1716                       frame, tilesize);
1717   MarkTileDirty(x / tilesize, y / tilesize);
1718 }
1719
1720 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1721                               int tilesize)
1722 {
1723   DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1724                               graphic, frame, tilesize);
1725   MarkTileDirty(x / tilesize, y / tilesize);
1726 }
1727
1728 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1729                          int tilesize)
1730 {
1731   Bitmap *src_bitmap;
1732   int src_x, src_y;
1733
1734   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1735   BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1736 }
1737
1738 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1739                                  int frame, int tilesize)
1740 {
1741   Bitmap *src_bitmap;
1742   int src_x, src_y;
1743
1744   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1745   BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1746 }
1747
1748 void DrawMiniGraphic(int x, int y, int graphic)
1749 {
1750   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1751   MarkTileDirty(x / 2, y / 2);
1752 }
1753
1754 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1755 {
1756   Bitmap *src_bitmap;
1757   int src_x, src_y;
1758
1759   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1760   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1761 }
1762
1763 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1764                                      int graphic, int frame,
1765                                      int cut_mode, int mask_mode)
1766 {
1767   Bitmap *src_bitmap;
1768   int src_x, src_y;
1769   int dst_x, dst_y;
1770   int width = TILEX, height = TILEY;
1771   int cx = 0, cy = 0;
1772
1773   if (dx || dy)                 // shifted graphic
1774   {
1775     if (x < BX1)                // object enters playfield from the left
1776     {
1777       x = BX1;
1778       width = dx;
1779       cx = TILEX - dx;
1780       dx = 0;
1781     }
1782     else if (x > BX2)           // object enters playfield from the right
1783     {
1784       x = BX2;
1785       width = -dx;
1786       dx = TILEX + dx;
1787     }
1788     else if (x == BX1 && dx < 0) // object leaves playfield to the left
1789     {
1790       width += dx;
1791       cx = -dx;
1792       dx = 0;
1793     }
1794     else if (x == BX2 && dx > 0) // object leaves playfield to the right
1795       width -= dx;
1796     else if (dx)                // general horizontal movement
1797       MarkTileDirty(x + SIGN(dx), y);
1798
1799     if (y < BY1)                // object enters playfield from the top
1800     {
1801       if (cut_mode == CUT_BELOW) // object completely above top border
1802         return;
1803
1804       y = BY1;
1805       height = dy;
1806       cy = TILEY - dy;
1807       dy = 0;
1808     }
1809     else if (y > BY2)           // object enters playfield from the bottom
1810     {
1811       y = BY2;
1812       height = -dy;
1813       dy = TILEY + dy;
1814     }
1815     else if (y == BY1 && dy < 0) // object leaves playfield to the top
1816     {
1817       height += dy;
1818       cy = -dy;
1819       dy = 0;
1820     }
1821     else if (dy > 0 && cut_mode == CUT_ABOVE)
1822     {
1823       if (y == BY2)             // object completely above bottom border
1824         return;
1825
1826       height = dy;
1827       cy = TILEY - dy;
1828       dy = TILEY;
1829       MarkTileDirty(x, y + 1);
1830     }                           // object leaves playfield to the bottom
1831     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1832       height -= dy;
1833     else if (dy)                // general vertical movement
1834       MarkTileDirty(x, y + SIGN(dy));
1835   }
1836
1837 #if DEBUG
1838   if (!IN_SCR_FIELD(x, y))
1839   {
1840     Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1841           x, y, graphic);
1842     Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1843
1844     return;
1845   }
1846 #endif
1847
1848   width = width * TILESIZE_VAR / TILESIZE;
1849   height = height * TILESIZE_VAR / TILESIZE;
1850   cx = cx * TILESIZE_VAR / TILESIZE;
1851   cy = cy * TILESIZE_VAR / TILESIZE;
1852   dx = dx * TILESIZE_VAR / TILESIZE;
1853   dy = dy * TILESIZE_VAR / TILESIZE;
1854
1855   if (width > 0 && height > 0)
1856   {
1857     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1858
1859     src_x += cx;
1860     src_y += cy;
1861
1862     dst_x = FX + x * TILEX_VAR + dx;
1863     dst_y = FY + y * TILEY_VAR + dy;
1864
1865     if (mask_mode == USE_MASKING)
1866       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1867                        dst_x, dst_y);
1868     else
1869       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1870                  dst_x, dst_y);
1871
1872     MarkTileDirty(x, y);
1873   }
1874 }
1875
1876 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1877                                      int graphic, int frame,
1878                                      int cut_mode, int mask_mode)
1879 {
1880   Bitmap *src_bitmap;
1881   int src_x, src_y;
1882   int dst_x, dst_y;
1883   int width = TILEX_VAR, height = TILEY_VAR;
1884   int x1 = x;
1885   int y1 = y;
1886   int x2 = x + SIGN(dx);
1887   int y2 = y + SIGN(dy);
1888
1889   // movement with two-tile animations must be sync'ed with movement position,
1890   // not with current GfxFrame (which can be higher when using slow movement)
1891   int anim_pos = (dx ? ABS(dx) : ABS(dy));
1892   int anim_frames = graphic_info[graphic].anim_frames;
1893
1894   // (we also need anim_delay here for movement animations with less frames)
1895   int anim_delay = graphic_info[graphic].anim_delay;
1896   int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1897
1898   boolean draw_start_tile = (cut_mode != CUT_ABOVE);    // only for falling!
1899   boolean draw_end_tile   = (cut_mode != CUT_BELOW);    // only for falling!
1900
1901   // re-calculate animation frame for two-tile movement animation
1902   frame = getGraphicAnimationFrame(graphic, sync_frame);
1903
1904   // check if movement start graphic inside screen area and should be drawn
1905   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1906   {
1907     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1908
1909     dst_x = FX + x1 * TILEX_VAR;
1910     dst_y = FY + y1 * TILEY_VAR;
1911
1912     if (mask_mode == USE_MASKING)
1913       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1914                        dst_x, dst_y);
1915     else
1916       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1917                  dst_x, dst_y);
1918
1919     MarkTileDirty(x1, y1);
1920   }
1921
1922   // check if movement end graphic inside screen area and should be drawn
1923   if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1924   {
1925     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1926
1927     dst_x = FX + x2 * TILEX_VAR;
1928     dst_y = FY + y2 * TILEY_VAR;
1929
1930     if (mask_mode == USE_MASKING)
1931       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1932                        dst_x, dst_y);
1933     else
1934       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1935                  dst_x, dst_y);
1936
1937     MarkTileDirty(x2, y2);
1938   }
1939 }
1940
1941 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1942                                int graphic, int frame,
1943                                int cut_mode, int mask_mode)
1944 {
1945   if (graphic < 0)
1946   {
1947     DrawGraphic(x, y, graphic, frame);
1948
1949     return;
1950   }
1951
1952   if (graphic_info[graphic].double_movement)    // EM style movement images
1953     DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1954   else
1955     DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1956 }
1957
1958 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1959                                        int graphic, int frame, int cut_mode)
1960 {
1961   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1962 }
1963
1964 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1965                           int cut_mode, int mask_mode)
1966 {
1967   int lx = LEVELX(x), ly = LEVELY(y);
1968   int graphic;
1969   int frame;
1970
1971   if (IN_LEV_FIELD(lx, ly))
1972   {
1973     SetRandomAnimationValue(lx, ly);
1974
1975     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1976     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1977
1978     // do not use double (EM style) movement graphic when not moving
1979     if (graphic_info[graphic].double_movement && !dx && !dy)
1980     {
1981       graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1982       frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1983     }
1984
1985     if (game.use_masked_elements && (dx || dy))
1986       mask_mode = USE_MASKING;
1987   }
1988   else  // border element
1989   {
1990     graphic = el2img(element);
1991     frame = getGraphicAnimationFrame(graphic, -1);
1992   }
1993
1994   if (element == EL_EXPANDABLE_WALL)
1995   {
1996     boolean left_stopped = FALSE, right_stopped = FALSE;
1997
1998     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
1999       left_stopped = TRUE;
2000     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2001       right_stopped = TRUE;
2002
2003     if (left_stopped && right_stopped)
2004       graphic = IMG_WALL;
2005     else if (left_stopped)
2006     {
2007       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2008       frame = graphic_info[graphic].anim_frames - 1;
2009     }
2010     else if (right_stopped)
2011     {
2012       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2013       frame = graphic_info[graphic].anim_frames - 1;
2014     }
2015   }
2016
2017   if (dx || dy)
2018     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2019   else if (mask_mode == USE_MASKING)
2020     DrawGraphicThruMask(x, y, graphic, frame);
2021   else
2022     DrawGraphic(x, y, graphic, frame);
2023 }
2024
2025 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2026                          int cut_mode, int mask_mode)
2027 {
2028   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2029     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2030                          cut_mode, mask_mode);
2031 }
2032
2033 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2034                               int cut_mode)
2035 {
2036   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2037 }
2038
2039 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2040                              int cut_mode)
2041 {
2042   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2043 }
2044
2045 void DrawLevelElementThruMask(int x, int y, int element)
2046 {
2047   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2048 }
2049
2050 void DrawLevelFieldThruMask(int x, int y)
2051 {
2052   DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2053 }
2054
2055 // !!! implementation of quicksand is totally broken !!!
2056 #define IS_CRUMBLED_TILE(x, y, e)                                       \
2057         (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
2058                              !IS_MOVING(x, y) ||                        \
2059                              (e) == EL_QUICKSAND_EMPTYING ||            \
2060                              (e) == EL_QUICKSAND_FAST_EMPTYING))
2061
2062 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2063                                                int graphic)
2064 {
2065   Bitmap *src_bitmap;
2066   int src_x, src_y;
2067   int width, height, cx, cy;
2068   int sx = SCREENX(x), sy = SCREENY(y);
2069   int crumbled_border_size = graphic_info[graphic].border_size;
2070   int crumbled_tile_size = graphic_info[graphic].tile_size;
2071   int crumbled_border_size_var =
2072     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2073   int i;
2074
2075   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2076
2077   for (i = 1; i < 4; i++)
2078   {
2079     int dxx = (i & 1 ? dx : 0);
2080     int dyy = (i & 2 ? dy : 0);
2081     int xx = x + dxx;
2082     int yy = y + dyy;
2083     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2084                    BorderElement);
2085
2086     // check if neighbour field is of same crumble type
2087     boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2088                     graphic_info[graphic].class ==
2089                     graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2090
2091     // return if check prevents inner corner
2092     if (same == (dxx == dx && dyy == dy))
2093       return;
2094   }
2095
2096   // if we reach this point, we have an inner corner
2097
2098   getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2099
2100   width  = crumbled_border_size_var;
2101   height = crumbled_border_size_var;
2102   cx = (dx > 0 ? TILESIZE_VAR - width  : 0);
2103   cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2104
2105   if (game.use_masked_elements)
2106   {
2107     int graphic0 = el2img(EL_EMPTY);
2108     int frame0 = getGraphicAnimationFrame(graphic0, GfxFrame[x][y]);
2109     Bitmap *src_bitmap0;
2110     int src_x0, src_y0;
2111
2112     getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2113
2114     BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2115                width, height,
2116                FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2117
2118     BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2119                      width, height,
2120                      FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2121   }
2122   else
2123     BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2124                width, height,
2125                FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2126 }
2127
2128 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2129                                           int dir)
2130 {
2131   Bitmap *src_bitmap;
2132   int src_x, src_y;
2133   int width, height, bx, by, cx, cy;
2134   int sx = SCREENX(x), sy = SCREENY(y);
2135   int crumbled_border_size = graphic_info[graphic].border_size;
2136   int crumbled_tile_size = graphic_info[graphic].tile_size;
2137   int crumbled_border_size_var =
2138     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2139   int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2140   int i;
2141
2142   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2143
2144   // only needed when using masked elements
2145   int graphic0 = el2img(EL_EMPTY);
2146   int frame0 = getGraphicAnimationFrame(graphic0, GfxFrame[x][y]);
2147   Bitmap *src_bitmap0;
2148   int src_x0, src_y0;
2149
2150   if (game.use_masked_elements)
2151     getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2152
2153   // draw simple, sloppy, non-corner-accurate crumbled border
2154
2155   width  = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2156   height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2157   cx = (dir == 2 ? crumbled_border_pos_var : 0);
2158   cy = (dir == 3 ? crumbled_border_pos_var : 0);
2159
2160   if (game.use_masked_elements)
2161   {
2162     BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2163                width, height,
2164                FX + sx * TILEX_VAR + cx,
2165                FY + sy * TILEY_VAR + cy);
2166
2167     BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2168                      width, height,
2169                      FX + sx * TILEX_VAR + cx,
2170                      FY + sy * TILEY_VAR + cy);
2171   }
2172   else
2173     BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2174                width, height,
2175                FX + sx * TILEX_VAR + cx,
2176                FY + sy * TILEY_VAR + cy);
2177
2178   // (remaining middle border part must be at least as big as corner part)
2179   if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2180       crumbled_border_size_var >= TILESIZE_VAR / 3)
2181     return;
2182
2183   // correct corners of crumbled border, if needed
2184
2185   for (i = -1; i <= 1; i += 2)
2186   {
2187     int xx = x + (dir == 0 || dir == 3 ? i : 0);
2188     int yy = y + (dir == 1 || dir == 2 ? i : 0);
2189     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2190                    BorderElement);
2191
2192     // check if neighbour field is of same crumble type
2193     if (IS_CRUMBLED_TILE(xx, yy, element) &&
2194         graphic_info[graphic].class ==
2195         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2196     {
2197       // no crumbled corner, but continued crumbled border
2198
2199       int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2200       int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2201       int b1 = (i == 1 ? crumbled_border_size_var :
2202                 TILESIZE_VAR - 2 * crumbled_border_size_var);
2203
2204       width  = crumbled_border_size_var;
2205       height = crumbled_border_size_var;
2206
2207       if (dir == 1 || dir == 2)
2208       {
2209         cx = c1;
2210         cy = c2;
2211         bx = cx;
2212         by = b1;
2213       }
2214       else
2215       {
2216         cx = c2;
2217         cy = c1;
2218         bx = b1;
2219         by = cy;
2220       }
2221
2222       if (game.use_masked_elements)
2223       {
2224         BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2225                    width, height,
2226                    FX + sx * TILEX_VAR + cx,
2227                    FY + sy * TILEY_VAR + cy);
2228
2229         BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2230                          width, height,
2231                          FX + sx * TILEX_VAR + cx,
2232                          FY + sy * TILEY_VAR + cy);
2233       }
2234       else
2235         BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2236                    width, height,
2237                    FX + sx * TILEX_VAR + cx,
2238                    FY + sy * TILEY_VAR + cy);
2239     }
2240   }
2241 }
2242
2243 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2244 {
2245   int sx = SCREENX(x), sy = SCREENY(y);
2246   int element;
2247   int i;
2248   static int xy[4][2] =
2249   {
2250     { 0, -1 },
2251     { -1, 0 },
2252     { +1, 0 },
2253     { 0, +1 }
2254   };
2255
2256   if (!IN_LEV_FIELD(x, y))
2257     return;
2258
2259   element = TILE_GFX_ELEMENT(x, y);
2260
2261   if (IS_CRUMBLED_TILE(x, y, element))          // crumble field itself
2262   {
2263     if (!IN_SCR_FIELD(sx, sy))
2264       return;
2265
2266     // crumble field borders towards direct neighbour fields
2267     for (i = 0; i < 4; i++)
2268     {
2269       int xx = x + xy[i][0];
2270       int yy = y + xy[i][1];
2271
2272       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2273                  BorderElement);
2274
2275       // check if neighbour field is of same crumble type
2276       if (IS_CRUMBLED_TILE(xx, yy, element) &&
2277           graphic_info[graphic].class ==
2278           graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2279         continue;
2280
2281       DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2282     }
2283
2284     // crumble inner field corners towards corner neighbour fields
2285     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2286         graphic_info[graphic].anim_frames == 2)
2287     {
2288       for (i = 0; i < 4; i++)
2289       {
2290         int dx = (i & 1 ? +1 : -1);
2291         int dy = (i & 2 ? +1 : -1);
2292
2293         DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2294       }
2295     }
2296
2297     MarkTileDirty(sx, sy);
2298   }
2299   else          // center field is not crumbled -- crumble neighbour fields
2300   {
2301     // crumble field borders of direct neighbour fields
2302     for (i = 0; i < 4; i++)
2303     {
2304       int xx = x + xy[i][0];
2305       int yy = y + xy[i][1];
2306       int sxx = sx + xy[i][0];
2307       int syy = sy + xy[i][1];
2308
2309       if (!IN_LEV_FIELD(xx, yy) ||
2310           !IN_SCR_FIELD(sxx, syy))
2311         continue;
2312
2313       // do not crumble fields that are being digged or snapped
2314       if (Tile[xx][yy] == EL_EMPTY ||
2315           Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2316         continue;
2317
2318       element = TILE_GFX_ELEMENT(xx, yy);
2319
2320       if (!IS_CRUMBLED_TILE(xx, yy, element))
2321         continue;
2322
2323       graphic = el_act2crm(element, ACTION_DEFAULT);
2324
2325       DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2326
2327       MarkTileDirty(sxx, syy);
2328     }
2329
2330     // crumble inner field corners of corner neighbour fields
2331     for (i = 0; i < 4; i++)
2332     {
2333       int dx = (i & 1 ? +1 : -1);
2334       int dy = (i & 2 ? +1 : -1);
2335       int xx = x + dx;
2336       int yy = y + dy;
2337       int sxx = sx + dx;
2338       int syy = sy + dy;
2339
2340       if (!IN_LEV_FIELD(xx, yy) ||
2341           !IN_SCR_FIELD(sxx, syy))
2342         continue;
2343
2344       if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2345         continue;
2346
2347       element = TILE_GFX_ELEMENT(xx, yy);
2348
2349       if (!IS_CRUMBLED_TILE(xx, yy, element))
2350         continue;
2351
2352       graphic = el_act2crm(element, ACTION_DEFAULT);
2353
2354       if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2355           graphic_info[graphic].anim_frames == 2)
2356         DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2357
2358       MarkTileDirty(sxx, syy);
2359     }
2360   }
2361 }
2362
2363 void DrawLevelFieldCrumbled(int x, int y)
2364 {
2365   int graphic;
2366
2367   if (!IN_LEV_FIELD(x, y))
2368     return;
2369
2370   if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2371       GfxElement[x][y] != EL_UNDEFINED &&
2372       GFX_CRUMBLED(GfxElement[x][y]))
2373   {
2374     DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2375
2376     return;
2377   }
2378
2379   graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2380
2381   DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2382 }
2383
2384 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2385                                    int step_frame)
2386 {
2387   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2388   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2389   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2390   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2391   int sx = SCREENX(x), sy = SCREENY(y);
2392
2393   DrawScreenGraphic(sx, sy, graphic1, frame1);
2394   DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2395 }
2396
2397 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2398 {
2399   int sx = SCREENX(x), sy = SCREENY(y);
2400   static int xy[4][2] =
2401   {
2402     { 0, -1 },
2403     { -1, 0 },
2404     { +1, 0 },
2405     { 0, +1 }
2406   };
2407   int i;
2408
2409   // crumble direct neighbour fields (required for field borders)
2410   for (i = 0; i < 4; i++)
2411   {
2412     int xx = x + xy[i][0];
2413     int yy = y + xy[i][1];
2414     int sxx = sx + xy[i][0];
2415     int syy = sy + xy[i][1];
2416
2417     if (!IN_LEV_FIELD(xx, yy) ||
2418         !IN_SCR_FIELD(sxx, syy) ||
2419         !GFX_CRUMBLED(Tile[xx][yy]) ||
2420         IS_MOVING(xx, yy))
2421       continue;
2422
2423     DrawLevelField(xx, yy);
2424   }
2425
2426   // crumble corner neighbour fields (required for inner field corners)
2427   for (i = 0; i < 4; i++)
2428   {
2429     int dx = (i & 1 ? +1 : -1);
2430     int dy = (i & 2 ? +1 : -1);
2431     int xx = x + dx;
2432     int yy = y + dy;
2433     int sxx = sx + dx;
2434     int syy = sy + dy;
2435
2436     if (!IN_LEV_FIELD(xx, yy) ||
2437         !IN_SCR_FIELD(sxx, syy) ||
2438         !GFX_CRUMBLED(Tile[xx][yy]) ||
2439         IS_MOVING(xx, yy))
2440       continue;
2441
2442     int element = TILE_GFX_ELEMENT(xx, yy);
2443     int graphic = el_act2crm(element, ACTION_DEFAULT);
2444
2445     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2446         graphic_info[graphic].anim_frames == 2)
2447       DrawLevelField(xx, yy);
2448   }
2449 }
2450
2451 static int getBorderElement(int x, int y)
2452 {
2453   int border[7][2] =
2454   {
2455     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
2456     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
2457     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
2458     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2459     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
2460     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
2461     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
2462   };
2463   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2464   int steel_position = (x == -1         && y == -1              ? 0 :
2465                         x == lev_fieldx && y == -1              ? 1 :
2466                         x == -1         && y == lev_fieldy      ? 2 :
2467                         x == lev_fieldx && y == lev_fieldy      ? 3 :
2468                         x == -1         || x == lev_fieldx      ? 4 :
2469                         y == -1         || y == lev_fieldy      ? 5 : 6);
2470
2471   return border[steel_position][steel_type];
2472 }
2473
2474 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2475 {
2476   if (game.use_masked_elements)
2477   {
2478     if (graphic != el2img(EL_EMPTY))
2479       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2480
2481     DrawGraphicThruMask(x, y, graphic, frame);
2482   }
2483   else
2484   {
2485     DrawGraphic(x, y, graphic, frame);
2486   }
2487 }
2488
2489 void DrawScreenElement(int x, int y, int element)
2490 {
2491   int mask_mode = NO_MASKING;
2492
2493   if (game.use_masked_elements)
2494   {
2495     int lx = LEVELX(x), ly = LEVELY(y);
2496
2497     if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2498     {
2499       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2500
2501       mask_mode = USE_MASKING;
2502     }
2503   }
2504
2505   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2506   DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2507 }
2508
2509 void DrawLevelElement(int x, int y, int element)
2510 {
2511   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2512     DrawScreenElement(SCREENX(x), SCREENY(y), element);
2513 }
2514
2515 void DrawScreenField(int x, int y)
2516 {
2517   int lx = LEVELX(x), ly = LEVELY(y);
2518   int element, content;
2519
2520   if (!IN_LEV_FIELD(lx, ly))
2521   {
2522     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2523       element = EL_EMPTY;
2524     else
2525       element = getBorderElement(lx, ly);
2526
2527     DrawScreenElement(x, y, element);
2528
2529     return;
2530   }
2531
2532   element = Tile[lx][ly];
2533   content = Store[lx][ly];
2534
2535   if (IS_MOVING(lx, ly))
2536   {
2537     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2538     boolean cut_mode = NO_CUTTING;
2539
2540     if (element == EL_QUICKSAND_EMPTYING ||
2541         element == EL_QUICKSAND_FAST_EMPTYING ||
2542         element == EL_MAGIC_WALL_EMPTYING ||
2543         element == EL_BD_MAGIC_WALL_EMPTYING ||
2544         element == EL_DC_MAGIC_WALL_EMPTYING ||
2545         element == EL_AMOEBA_DROPPING)
2546       cut_mode = CUT_ABOVE;
2547     else if (element == EL_QUICKSAND_FILLING ||
2548              element == EL_QUICKSAND_FAST_FILLING ||
2549              element == EL_MAGIC_WALL_FILLING ||
2550              element == EL_BD_MAGIC_WALL_FILLING ||
2551              element == EL_DC_MAGIC_WALL_FILLING)
2552       cut_mode = CUT_BELOW;
2553
2554     if (cut_mode == CUT_ABOVE)
2555       DrawScreenElement(x, y, element);
2556     else
2557       DrawScreenElement(x, y, EL_EMPTY);
2558
2559     if (cut_mode != CUT_BELOW && game.use_masked_elements)
2560     {
2561       int dir = MovDir[lx][ly];
2562       int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2563       int newy = y + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2564
2565       if (IN_SCR_FIELD(newx, newy))
2566         DrawScreenElement(newx, newy, EL_EMPTY);
2567     }
2568
2569     if (horiz_move)
2570       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2571     else if (cut_mode == NO_CUTTING)
2572       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2573     else
2574     {
2575       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2576
2577       if (cut_mode == CUT_BELOW &&
2578           IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2579         DrawLevelElement(lx, ly + 1, element);
2580     }
2581
2582     if (content == EL_ACID)
2583     {
2584       int dir = MovDir[lx][ly];
2585       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2586       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2587
2588       DrawLevelElementThruMask(newlx, newly, EL_ACID);
2589
2590       // prevent target field from being drawn again (but without masking)
2591       // (this would happen if target field is scanned after moving element)
2592       Stop[newlx][newly] = TRUE;
2593     }
2594   }
2595   else if (IS_BLOCKED(lx, ly))
2596   {
2597     int oldx, oldy;
2598     int sx, sy;
2599     int horiz_move;
2600     boolean cut_mode = NO_CUTTING;
2601     int element_old, content_old;
2602
2603     Blocked2Moving(lx, ly, &oldx, &oldy);
2604     sx = SCREENX(oldx);
2605     sy = SCREENY(oldy);
2606     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2607                   MovDir[oldx][oldy] == MV_RIGHT);
2608
2609     element_old = Tile[oldx][oldy];
2610     content_old = Store[oldx][oldy];
2611
2612     if (element_old == EL_QUICKSAND_EMPTYING ||
2613         element_old == EL_QUICKSAND_FAST_EMPTYING ||
2614         element_old == EL_MAGIC_WALL_EMPTYING ||
2615         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2616         element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2617         element_old == EL_AMOEBA_DROPPING)
2618       cut_mode = CUT_ABOVE;
2619
2620     DrawScreenElement(x, y, EL_EMPTY);
2621
2622     if (horiz_move)
2623       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2624                                NO_CUTTING);
2625     else if (cut_mode == NO_CUTTING)
2626       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2627                                cut_mode);
2628     else
2629       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2630                                cut_mode);
2631   }
2632   else if (IS_DRAWABLE(element))
2633     DrawScreenElement(x, y, element);
2634   else
2635     DrawScreenElement(x, y, EL_EMPTY);
2636 }
2637
2638 void DrawLevelField(int x, int y)
2639 {
2640   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2641     DrawScreenField(SCREENX(x), SCREENY(y));
2642   else if (IS_MOVING(x, y))
2643   {
2644     int newx,newy;
2645
2646     Moving2Blocked(x, y, &newx, &newy);
2647     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2648       DrawScreenField(SCREENX(newx), SCREENY(newy));
2649   }
2650   else if (IS_BLOCKED(x, y))
2651   {
2652     int oldx, oldy;
2653
2654     Blocked2Moving(x, y, &oldx, &oldy);
2655     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2656       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2657   }
2658 }
2659
2660 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2661                                 int (*el2img_function)(int), boolean masked,
2662                                 int element_bits_draw)
2663 {
2664   int element_base = map_mm_wall_element(element);
2665   int element_bits = (IS_DF_WALL(element) ?
2666                       element - EL_DF_WALL_START :
2667                       IS_MM_WALL(element) ?
2668                       element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2669   int graphic = el2img_function(element_base);
2670   int tilesize_draw = tilesize / 2;
2671   Bitmap *src_bitmap;
2672   int src_x, src_y;
2673   int i;
2674
2675   getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2676
2677   for (i = 0; i < 4; i++)
2678   {
2679     int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2680     int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2681
2682     if (!(element_bits_draw & (1 << i)))
2683       continue;
2684
2685     if (element_bits & (1 << i))
2686     {
2687       if (masked)
2688         BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2689                          tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2690       else
2691         BlitBitmap(src_bitmap, drawto, src_x, src_y,
2692                    tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2693     }
2694     else
2695     {
2696       if (!masked)
2697         ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2698                        tilesize_draw, tilesize_draw);
2699     }
2700   }
2701 }
2702
2703 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2704                            boolean masked, int element_bits_draw)
2705 {
2706   DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2707                       element, tilesize, el2edimg, masked, element_bits_draw);
2708 }
2709
2710 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2711                              int (*el2img_function)(int))
2712 {
2713   DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2714                       0x000f);
2715 }
2716
2717 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2718                                 boolean masked)
2719 {
2720   if (IS_MM_WALL(element))
2721   {
2722     DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2723                         element, tilesize, el2edimg, masked, 0x000f);
2724   }
2725   else
2726   {
2727     int graphic = el2edimg(element);
2728
2729     if (masked)
2730       DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2731     else
2732       DrawSizedGraphic(x, y, graphic, 0, tilesize);
2733   }
2734 }
2735
2736 void DrawSizedElement(int x, int y, int element, int tilesize)
2737 {
2738   DrawSizedElementExt(x, y, element, tilesize, FALSE);
2739 }
2740
2741 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2742 {
2743   DrawSizedElementExt(x, y, element, tilesize, TRUE);
2744 }
2745
2746 void DrawMiniElement(int x, int y, int element)
2747 {
2748   int graphic;
2749
2750   graphic = el2edimg(element);
2751   DrawMiniGraphic(x, y, graphic);
2752 }
2753
2754 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2755                             int tilesize)
2756 {
2757   int x = sx + scroll_x, y = sy + scroll_y;
2758
2759   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2760     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2761   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2762     DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2763   else
2764     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2765 }
2766
2767 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2768 {
2769   int x = sx + scroll_x, y = sy + scroll_y;
2770
2771   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2772     DrawMiniElement(sx, sy, EL_EMPTY);
2773   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2774     DrawMiniElement(sx, sy, Tile[x][y]);
2775   else
2776     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2777 }
2778
2779 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2780                                         int x, int y, int xsize, int ysize,
2781                                         int tile_width, int tile_height)
2782 {
2783   Bitmap *src_bitmap;
2784   int src_x, src_y;
2785   int dst_x = startx + x * tile_width;
2786   int dst_y = starty + y * tile_height;
2787   int width  = graphic_info[graphic].width;
2788   int height = graphic_info[graphic].height;
2789   int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
2790   int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2791   int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
2792   int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2793   int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
2794   int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2795   boolean draw_masked = graphic_info[graphic].draw_masked;
2796
2797   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2798
2799   if (src_bitmap == NULL || width < tile_width || height < tile_height)
2800   {
2801     ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2802     return;
2803   }
2804
2805   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
2806             inner_sx + (x - 1) * tile_width  % inner_width);
2807   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2808             inner_sy + (y - 1) * tile_height % inner_height);
2809
2810   if (draw_masked)
2811     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2812                      dst_x, dst_y);
2813   else
2814     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2815                dst_x, dst_y);
2816 }
2817
2818 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2819                                    int x, int y, int xsize, int ysize,
2820                                    int font_nr)
2821 {
2822   int font_width  = getFontWidth(font_nr);
2823   int font_height = getFontHeight(font_nr);
2824
2825   DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2826                               font_width, font_height);
2827 }
2828
2829 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2830 {
2831   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2832   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2833   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2834   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2835   boolean no_delay = (tape.warp_forward);
2836   unsigned int anim_delay = 0;
2837   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2838   int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2839   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2840   int font_width = getFontWidth(font_nr);
2841   int font_height = getFontHeight(font_nr);
2842   int max_xsize = level.envelope[envelope_nr].xsize;
2843   int max_ysize = level.envelope[envelope_nr].ysize;
2844   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2845   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2846   int xend = max_xsize;
2847   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2848   int xstep = (xstart < xend ? 1 : 0);
2849   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2850   int start = 0;
2851   int end = MAX(xend - xstart, yend - ystart);
2852   int i;
2853
2854   for (i = start; i <= end; i++)
2855   {
2856     int last_frame = end;       // last frame of this "for" loop
2857     int x = xstart + i * xstep;
2858     int y = ystart + i * ystep;
2859     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2860     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2861     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2862     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2863     int xx, yy;
2864
2865     SetDrawtoField(DRAW_TO_FIELDBUFFER);
2866
2867     BlitScreenToBitmap(backbuffer);
2868
2869     SetDrawtoField(DRAW_TO_BACKBUFFER);
2870
2871     for (yy = 0; yy < ysize; yy++)
2872       for (xx = 0; xx < xsize; xx++)
2873         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2874
2875     DrawTextBuffer(sx + font_width, sy + font_height,
2876                    level.envelope[envelope_nr].text, font_nr, max_xsize,
2877                    xsize - 2, ysize - 2, 0, mask_mode,
2878                    level.envelope[envelope_nr].autowrap,
2879                    level.envelope[envelope_nr].centered, FALSE);
2880
2881     redraw_mask |= REDRAW_FIELD;
2882     BackToFront();
2883
2884     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2885   }
2886
2887   ClearAutoRepeatKeyEvents();
2888 }
2889
2890 void ShowEnvelope(int envelope_nr)
2891 {
2892   int element = EL_ENVELOPE_1 + envelope_nr;
2893   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2894   int sound_opening = element_info[element].sound[ACTION_OPENING];
2895   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2896   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2897   boolean no_delay = (tape.warp_forward);
2898   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2899   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2900   int anim_mode = graphic_info[graphic].anim_mode;
2901   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2902                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2903   boolean overlay_enabled = GetOverlayEnabled();
2904
2905   game.envelope_active = TRUE;  // needed for RedrawPlayfield() events
2906
2907   SetOverlayEnabled(FALSE);
2908   UnmapAllGadgets();
2909
2910   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2911
2912   if (anim_mode == ANIM_DEFAULT)
2913     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2914
2915   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2916
2917   if (tape.playing)
2918     Delay_WithScreenUpdates(wait_delay_value);
2919   else
2920     WaitForEventToContinue();
2921
2922   RemapAllGadgets();
2923   SetOverlayEnabled(overlay_enabled);
2924
2925   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2926
2927   if (anim_mode != ANIM_NONE)
2928     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2929
2930   if (anim_mode == ANIM_DEFAULT)
2931     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2932
2933   game.envelope_active = FALSE;
2934
2935   SetDrawtoField(DRAW_TO_FIELDBUFFER);
2936
2937   redraw_mask |= REDRAW_FIELD;
2938   BackToFront();
2939 }
2940
2941 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2942                                            int xsize, int ysize)
2943 {
2944   if (!global.use_envelope_request ||
2945       request.sort_priority <= 0)
2946     return;
2947
2948   if (request.bitmap == NULL ||
2949       xsize > request.xsize ||
2950       ysize > request.ysize)
2951   {
2952     if (request.bitmap != NULL)
2953       FreeBitmap(request.bitmap);
2954
2955     request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2956
2957     SDL_Surface *surface = request.bitmap->surface;
2958
2959     if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2960       Fail("SDLGetNativeSurface() failed");
2961   }
2962
2963   BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2964
2965   SDLFreeBitmapTextures(request.bitmap);
2966   SDLCreateBitmapTextures(request.bitmap);
2967
2968   // set envelope request run-time values
2969   request.sx = sx;
2970   request.sy = sy;
2971   request.xsize = xsize;
2972   request.ysize = ysize;
2973 }
2974
2975 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
2976 {
2977   if (global.use_envelope_request &&
2978       game.request_active_or_moving &&
2979       request.sort_priority > 0 &&
2980       drawing_target == DRAW_TO_SCREEN &&
2981       drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
2982   {
2983     BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
2984                  request.sx, request.sy);
2985   }
2986 }
2987
2988 static void setRequestBasePosition(int *x, int *y)
2989 {
2990   int sx_base, sy_base;
2991
2992   if (request.x != -1)
2993     sx_base = request.x;
2994   else if (request.align == ALIGN_LEFT)
2995     sx_base = SX;
2996   else if (request.align == ALIGN_RIGHT)
2997     sx_base = SX + SXSIZE;
2998   else
2999     sx_base = SX + SXSIZE / 2;
3000
3001   if (request.y != -1)
3002     sy_base = request.y;
3003   else if (request.valign == VALIGN_TOP)
3004     sy_base = SY;
3005   else if (request.valign == VALIGN_BOTTOM)
3006     sy_base = SY + SYSIZE;
3007   else
3008     sy_base = SY + SYSIZE / 2;
3009
3010   *x = sx_base;
3011   *y = sy_base;
3012 }
3013
3014 static void setRequestPositionExt(int *x, int *y, int width, int height,
3015                                   boolean add_border_size)
3016 {
3017   int border_size = request.border_size;
3018   int sx_base, sy_base;
3019   int sx, sy;
3020
3021   setRequestBasePosition(&sx_base, &sy_base);
3022
3023   if (request.align == ALIGN_LEFT)
3024     sx = sx_base;
3025   else if (request.align == ALIGN_RIGHT)
3026     sx = sx_base - width;
3027   else
3028     sx = sx_base - width  / 2;
3029
3030   if (request.valign == VALIGN_TOP)
3031     sy = sy_base;
3032   else if (request.valign == VALIGN_BOTTOM)
3033     sy = sy_base - height;
3034   else
3035     sy = sy_base - height / 2;
3036
3037   sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3038   sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3039
3040   if (add_border_size)
3041   {
3042     sx += border_size;
3043     sy += border_size;
3044   }
3045
3046   *x = sx;
3047   *y = sy;
3048 }
3049
3050 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3051 {
3052   setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3053 }
3054
3055 static void DrawEnvelopeRequest(char *text)
3056 {
3057   char *text_final = text;
3058   char *text_door_style = NULL;
3059   int graphic = IMG_BACKGROUND_REQUEST;
3060   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3061   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3062   int font_nr = FONT_REQUEST;
3063   int font_width = getFontWidth(font_nr);
3064   int font_height = getFontHeight(font_nr);
3065   int border_size = request.border_size;
3066   int line_spacing = request.line_spacing;
3067   int line_height = font_height + line_spacing;
3068   int max_text_width  = request.width  - 2 * border_size;
3069   int max_text_height = request.height - 2 * border_size;
3070   int line_length = max_text_width  / font_width;
3071   int max_lines   = max_text_height / line_height;
3072   int text_width = line_length * font_width;
3073   int width = request.width;
3074   int height = request.height;
3075   int tile_size = MAX(request.step_offset, 1);
3076   int x_steps = width  / tile_size;
3077   int y_steps = height / tile_size;
3078   int sx_offset = border_size;
3079   int sy_offset = border_size;
3080   int sx, sy;
3081   int i, x, y;
3082
3083   if (request.centered)
3084     sx_offset = (request.width - text_width) / 2;
3085
3086   if (request.wrap_single_words && !request.autowrap)
3087   {
3088     char *src_text_ptr, *dst_text_ptr;
3089
3090     text_door_style = checked_malloc(2 * strlen(text) + 1);
3091
3092     src_text_ptr = text;
3093     dst_text_ptr = text_door_style;
3094
3095     while (*src_text_ptr)
3096     {
3097       if (*src_text_ptr == ' ' ||
3098           *src_text_ptr == '?' ||
3099           *src_text_ptr == '!')
3100         *dst_text_ptr++ = '\n';
3101
3102       if (*src_text_ptr != ' ')
3103         *dst_text_ptr++ = *src_text_ptr;
3104
3105       src_text_ptr++;
3106     }
3107
3108     *dst_text_ptr = '\0';
3109
3110     text_final = text_door_style;
3111   }
3112
3113   setRequestPosition(&sx, &sy, FALSE);
3114
3115   ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3116
3117   for (y = 0; y < y_steps; y++)
3118     for (x = 0; x < x_steps; x++)
3119       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3120                                   x, y, x_steps, y_steps,
3121                                   tile_size, tile_size);
3122
3123   // force DOOR font inside door area
3124   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3125
3126   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3127                  line_length, -1, max_lines, line_spacing, mask_mode,
3128                  request.autowrap, request.centered, FALSE);
3129
3130   ResetFontStatus();
3131
3132   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3133     RedrawGadget(tool_gadget[i]);
3134
3135   // store readily prepared envelope request for later use when animating
3136   BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3137
3138   PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3139
3140   if (text_door_style)
3141     free(text_door_style);
3142 }
3143
3144 static void AnimateEnvelopeRequest(int anim_mode, int action)
3145 {
3146   int graphic = IMG_BACKGROUND_REQUEST;
3147   boolean draw_masked = graphic_info[graphic].draw_masked;
3148   int delay_value_normal = request.step_delay;
3149   int delay_value_fast = delay_value_normal / 2;
3150   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3151   boolean no_delay = (tape.warp_forward);
3152   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3153   int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3154   unsigned int anim_delay = 0;
3155
3156   int tile_size = MAX(request.step_offset, 1);
3157   int max_xsize = request.width  / tile_size;
3158   int max_ysize = request.height / tile_size;
3159   int max_xsize_inner = max_xsize - 2;
3160   int max_ysize_inner = max_ysize - 2;
3161
3162   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3163   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3164   int xend = max_xsize_inner;
3165   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3166   int xstep = (xstart < xend ? 1 : 0);
3167   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3168   int start = 0;
3169   int end = MAX(xend - xstart, yend - ystart);
3170   int i;
3171
3172   if (setup.quick_doors)
3173   {
3174     xstart = xend;
3175     ystart = yend;
3176     end = 0;
3177   }
3178
3179   for (i = start; i <= end; i++)
3180   {
3181     int last_frame = end;       // last frame of this "for" loop
3182     int x = xstart + i * xstep;
3183     int y = ystart + i * ystep;
3184     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3185     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3186     int xsize_size_left = (xsize - 1) * tile_size;
3187     int ysize_size_top  = (ysize - 1) * tile_size;
3188     int max_xsize_pos = (max_xsize - 1) * tile_size;
3189     int max_ysize_pos = (max_ysize - 1) * tile_size;
3190     int width  = xsize * tile_size;
3191     int height = ysize * tile_size;
3192     int src_x, src_y;
3193     int dst_x, dst_y;
3194     int xx, yy;
3195
3196     setRequestPosition(&src_x, &src_y, FALSE);
3197     setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3198
3199     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3200
3201     for (yy = 0; yy < 2; yy++)
3202     {
3203       for (xx = 0; xx < 2; xx++)
3204       {
3205         int src_xx = src_x + xx * max_xsize_pos;
3206         int src_yy = src_y + yy * max_ysize_pos;
3207         int dst_xx = dst_x + xx * xsize_size_left;
3208         int dst_yy = dst_y + yy * ysize_size_top;
3209         int xx_size = (xx ? tile_size : xsize_size_left);
3210         int yy_size = (yy ? tile_size : ysize_size_top);
3211
3212         if (draw_masked)
3213           BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3214                            src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3215         else
3216           BlitBitmap(bitmap_db_store_2, backbuffer,
3217                      src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3218       }
3219     }
3220
3221     PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3222
3223     redraw_mask |= REDRAW_FIELD;
3224
3225     BackToFront();
3226
3227     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3228   }
3229
3230   ClearAutoRepeatKeyEvents();
3231 }
3232
3233 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3234 {
3235   int graphic = IMG_BACKGROUND_REQUEST;
3236   int sound_opening = SND_REQUEST_OPENING;
3237   int sound_closing = SND_REQUEST_CLOSING;
3238   int anim_mode_1 = request.anim_mode;                  // (higher priority)
3239   int anim_mode_2 = graphic_info[graphic].anim_mode;    // (lower priority)
3240   int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3241   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3242                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3243
3244   if (game_status == GAME_MODE_PLAYING)
3245     BlitScreenToBitmap(backbuffer);
3246
3247   SetDrawtoField(DRAW_TO_BACKBUFFER);
3248
3249   // SetDrawBackgroundMask(REDRAW_NONE);
3250
3251   if (action == ACTION_OPENING)
3252   {
3253     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3254
3255     if (req_state & REQ_ASK)
3256     {
3257       MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3258       MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3259       MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3260       MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3261     }
3262     else if (req_state & REQ_CONFIRM)
3263     {
3264       MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3265       MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3266     }
3267     else if (req_state & REQ_PLAYER)
3268     {
3269       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3270       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3271       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3272       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3273     }
3274
3275     DrawEnvelopeRequest(text);
3276   }
3277
3278   game.envelope_active = TRUE;  // needed for RedrawPlayfield() events
3279
3280   if (action == ACTION_OPENING)
3281   {
3282     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3283
3284     if (anim_mode == ANIM_DEFAULT)
3285       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3286
3287     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3288   }
3289   else
3290   {
3291     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3292
3293     if (anim_mode != ANIM_NONE)
3294       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3295
3296     if (anim_mode == ANIM_DEFAULT)
3297       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3298   }
3299
3300   game.envelope_active = FALSE;
3301
3302   if (action == ACTION_CLOSING)
3303     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3304
3305   // SetDrawBackgroundMask(last_draw_background_mask);
3306
3307   redraw_mask |= REDRAW_FIELD;
3308
3309   BackToFront();
3310
3311   if (action == ACTION_CLOSING &&
3312       game_status == GAME_MODE_PLAYING &&
3313       level.game_engine_type == GAME_ENGINE_TYPE_RND)
3314     SetDrawtoField(DRAW_TO_FIELDBUFFER);
3315 }
3316
3317 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3318 {
3319   if (IS_MM_WALL(element))
3320   {
3321     DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3322   }
3323   else
3324   {
3325     Bitmap *src_bitmap;
3326     int src_x, src_y;
3327     int graphic = el2preimg(element);
3328
3329     getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3330     BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3331                dst_x, dst_y);
3332   }
3333 }
3334
3335 void DrawLevel(int draw_background_mask)
3336 {
3337   int x,y;
3338
3339   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3340   SetDrawBackgroundMask(draw_background_mask);
3341
3342   ClearField();
3343
3344   for (x = BX1; x <= BX2; x++)
3345     for (y = BY1; y <= BY2; y++)
3346       DrawScreenField(x, y);
3347
3348   redraw_mask |= REDRAW_FIELD;
3349 }
3350
3351 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3352                     int tilesize)
3353 {
3354   int x,y;
3355
3356   for (x = 0; x < size_x; x++)
3357     for (y = 0; y < size_y; y++)
3358       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3359
3360   redraw_mask |= REDRAW_FIELD;
3361 }
3362
3363 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3364 {
3365   int x,y;
3366
3367   for (x = 0; x < size_x; x++)
3368     for (y = 0; y < size_y; y++)
3369       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3370
3371   redraw_mask |= REDRAW_FIELD;
3372 }
3373
3374 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3375 {
3376   boolean show_level_border = (BorderElement != EL_EMPTY);
3377   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3378   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3379   int tile_size = preview.tile_size;
3380   int preview_width  = preview.xsize * tile_size;
3381   int preview_height = preview.ysize * tile_size;
3382   int real_preview_xsize = MIN(level_xsize, preview.xsize);
3383   int real_preview_ysize = MIN(level_ysize, preview.ysize);
3384   int real_preview_width  = real_preview_xsize * tile_size;
3385   int real_preview_height = real_preview_ysize * tile_size;
3386   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3387   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3388   int x, y;
3389
3390   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3391     return;
3392
3393   DrawBackground(dst_x, dst_y, preview_width, preview_height);
3394
3395   dst_x += (preview_width  - real_preview_width)  / 2;
3396   dst_y += (preview_height - real_preview_height) / 2;
3397
3398   for (x = 0; x < real_preview_xsize; x++)
3399   {
3400     for (y = 0; y < real_preview_ysize; y++)
3401     {
3402       int lx = from_x + x + (show_level_border ? -1 : 0);
3403       int ly = from_y + y + (show_level_border ? -1 : 0);
3404       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3405                      getBorderElement(lx, ly));
3406
3407       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3408                          element, tile_size);
3409     }
3410   }
3411
3412   redraw_mask |= REDRAW_FIELD;
3413 }
3414
3415 #define MICROLABEL_EMPTY                0
3416 #define MICROLABEL_LEVEL_NAME           1
3417 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
3418 #define MICROLABEL_LEVEL_AUTHOR         3
3419 #define MICROLABEL_IMPORTED_FROM_HEAD   4
3420 #define MICROLABEL_IMPORTED_FROM        5
3421 #define MICROLABEL_IMPORTED_BY_HEAD     6
3422 #define MICROLABEL_IMPORTED_BY          7
3423
3424 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3425 {
3426   int max_text_width = SXSIZE;
3427   int font_width = getFontWidth(font_nr);
3428
3429   if (pos->align == ALIGN_CENTER)
3430     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3431   else if (pos->align == ALIGN_RIGHT)
3432     max_text_width = pos->x;
3433   else
3434     max_text_width = SXSIZE - pos->x;
3435
3436   return max_text_width / font_width;
3437 }
3438
3439 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3440 {
3441   char label_text[MAX_OUTPUT_LINESIZE + 1];
3442   int max_len_label_text;
3443   int font_nr = pos->font;
3444   int i;
3445
3446   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3447     return;
3448
3449   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3450       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3451       mode == MICROLABEL_IMPORTED_BY_HEAD)
3452     font_nr = pos->font_alt;
3453
3454   max_len_label_text = getMaxTextLength(pos, font_nr);
3455
3456   if (pos->size != -1)
3457     max_len_label_text = pos->size;
3458
3459   for (i = 0; i < max_len_label_text; i++)
3460     label_text[i] = ' ';
3461   label_text[max_len_label_text] = '\0';
3462
3463   if (strlen(label_text) > 0)
3464     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3465
3466   strncpy(label_text,
3467           (mode == MICROLABEL_LEVEL_NAME ? level.name :
3468            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3469            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3470            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3471            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3472            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3473            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3474           max_len_label_text);
3475   label_text[max_len_label_text] = '\0';
3476
3477   if (strlen(label_text) > 0)
3478     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3479
3480   redraw_mask |= REDRAW_FIELD;
3481 }
3482
3483 static void DrawPreviewLevelLabel(int mode)
3484 {
3485   DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3486 }
3487
3488 static void DrawPreviewLevelInfo(int mode)
3489 {
3490   if (mode == MICROLABEL_LEVEL_NAME)
3491     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3492   else if (mode == MICROLABEL_LEVEL_AUTHOR)
3493     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3494 }
3495
3496 static void DrawPreviewLevelExt(boolean restart)
3497 {
3498   static unsigned int scroll_delay = 0;
3499   static unsigned int label_delay = 0;
3500   static int from_x, from_y, scroll_direction;
3501   static int label_state, label_counter;
3502   unsigned int scroll_delay_value = preview.step_delay;
3503   boolean show_level_border = (BorderElement != EL_EMPTY);
3504   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3505   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3506
3507   if (restart)
3508   {
3509     from_x = 0;
3510     from_y = 0;
3511
3512     if (preview.anim_mode == ANIM_CENTERED)
3513     {
3514       if (level_xsize > preview.xsize)
3515         from_x = (level_xsize - preview.xsize) / 2;
3516       if (level_ysize > preview.ysize)
3517         from_y = (level_ysize - preview.ysize) / 2;
3518     }
3519
3520     from_x += preview.xoffset;
3521     from_y += preview.yoffset;
3522
3523     scroll_direction = MV_RIGHT;
3524     label_state = 1;
3525     label_counter = 0;
3526
3527     DrawPreviewLevelPlayfield(from_x, from_y);
3528     DrawPreviewLevelLabel(label_state);
3529
3530     DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3531     DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3532
3533     // initialize delay counters
3534     DelayReached(&scroll_delay, 0);
3535     DelayReached(&label_delay, 0);
3536
3537     if (leveldir_current->name)
3538     {
3539       struct TextPosInfo *pos = &menu.main.text.level_info_1;
3540       char label_text[MAX_OUTPUT_LINESIZE + 1];
3541       int font_nr = pos->font;
3542       int max_len_label_text = getMaxTextLength(pos, font_nr);
3543
3544       if (pos->size != -1)
3545         max_len_label_text = pos->size;
3546
3547       strncpy(label_text, leveldir_current->name, max_len_label_text);
3548       label_text[max_len_label_text] = '\0';
3549
3550       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3551         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3552     }
3553
3554     return;
3555   }
3556
3557   // scroll preview level, if needed
3558   if (preview.anim_mode != ANIM_NONE &&
3559       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3560       DelayReached(&scroll_delay, scroll_delay_value))
3561   {
3562     switch (scroll_direction)
3563     {
3564       case MV_LEFT:
3565         if (from_x > 0)
3566         {
3567           from_x -= preview.step_offset;
3568           from_x = (from_x < 0 ? 0 : from_x);
3569         }
3570         else
3571           scroll_direction = MV_UP;
3572         break;
3573
3574       case MV_RIGHT:
3575         if (from_x < level_xsize - preview.xsize)
3576         {
3577           from_x += preview.step_offset;
3578           from_x = (from_x > level_xsize - preview.xsize ?
3579                     level_xsize - preview.xsize : from_x);
3580         }
3581         else
3582           scroll_direction = MV_DOWN;
3583         break;
3584
3585       case MV_UP:
3586         if (from_y > 0)
3587         {
3588           from_y -= preview.step_offset;
3589           from_y = (from_y < 0 ? 0 : from_y);
3590         }
3591         else
3592           scroll_direction = MV_RIGHT;
3593         break;
3594
3595       case MV_DOWN:
3596         if (from_y < level_ysize - preview.ysize)
3597         {
3598           from_y += preview.step_offset;
3599           from_y = (from_y > level_ysize - preview.ysize ?
3600                     level_ysize - preview.ysize : from_y);
3601         }
3602         else
3603           scroll_direction = MV_LEFT;
3604         break;
3605
3606       default:
3607         break;
3608     }
3609
3610     DrawPreviewLevelPlayfield(from_x, from_y);
3611   }
3612
3613   // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3614   // redraw micro level label, if needed
3615   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3616       !strEqual(level.author, ANONYMOUS_NAME) &&
3617       !strEqual(level.author, leveldir_current->name) &&
3618       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3619   {
3620     int max_label_counter = 23;
3621
3622     if (leveldir_current->imported_from != NULL &&
3623         strlen(leveldir_current->imported_from) > 0)
3624       max_label_counter += 14;
3625     if (leveldir_current->imported_by != NULL &&
3626         strlen(leveldir_current->imported_by) > 0)
3627       max_label_counter += 14;
3628
3629     label_counter = (label_counter + 1) % max_label_counter;
3630     label_state = (label_counter >= 0 && label_counter <= 7 ?
3631                    MICROLABEL_LEVEL_NAME :
3632                    label_counter >= 9 && label_counter <= 12 ?
3633                    MICROLABEL_LEVEL_AUTHOR_HEAD :
3634                    label_counter >= 14 && label_counter <= 21 ?
3635                    MICROLABEL_LEVEL_AUTHOR :
3636                    label_counter >= 23 && label_counter <= 26 ?
3637                    MICROLABEL_IMPORTED_FROM_HEAD :
3638                    label_counter >= 28 && label_counter <= 35 ?
3639                    MICROLABEL_IMPORTED_FROM :
3640                    label_counter >= 37 && label_counter <= 40 ?
3641                    MICROLABEL_IMPORTED_BY_HEAD :
3642                    label_counter >= 42 && label_counter <= 49 ?
3643                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3644
3645     if (leveldir_current->imported_from == NULL &&
3646         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3647          label_state == MICROLABEL_IMPORTED_FROM))
3648       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3649                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3650
3651     DrawPreviewLevelLabel(label_state);
3652   }
3653 }
3654
3655 void DrawPreviewPlayers(void)
3656 {
3657   if (game_status != GAME_MODE_MAIN)
3658     return;
3659
3660   // do not draw preview players if level preview redefined, but players aren't
3661   if (preview.redefined && !menu.main.preview_players.redefined)
3662     return;
3663
3664   boolean player_found[MAX_PLAYERS];
3665   int num_players = 0;
3666   int i, x, y;
3667
3668   for (i = 0; i < MAX_PLAYERS; i++)
3669     player_found[i] = FALSE;
3670
3671   // check which players can be found in the level (simple approach)
3672   for (x = 0; x < lev_fieldx; x++)
3673   {
3674     for (y = 0; y < lev_fieldy; y++)
3675     {
3676       int element = level.field[x][y];
3677
3678       if (ELEM_IS_PLAYER(element))
3679       {
3680         int player_nr = GET_PLAYER_NR(element);
3681
3682         player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3683
3684         if (!player_found[player_nr])
3685           num_players++;
3686
3687         player_found[player_nr] = TRUE;
3688       }
3689     }
3690   }
3691
3692   struct TextPosInfo *pos = &menu.main.preview_players;
3693   int tile_size = pos->tile_size;
3694   int border_size = pos->border_size;
3695   int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3696   int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3697   int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3698   int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3699   int max_players_width  = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3700   int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3701   int all_players_width  = (num_players - 1) * player_xoffset + tile_size;
3702   int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3703   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3704   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3705   int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width,  pos->align);
3706   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3707
3708   // clear area in which the players will be drawn
3709   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3710                              max_players_width, max_players_height);
3711
3712   if (!network.enabled && !setup.team_mode)
3713     return;
3714
3715   // only draw players if level is suited for team mode
3716   if (num_players < 2)
3717     return;
3718
3719   // draw all players that were found in the level
3720   for (i = 0; i < MAX_PLAYERS; i++)
3721   {
3722     if (player_found[i])
3723     {
3724       int graphic = el2img(EL_PLAYER_1 + i);
3725
3726       DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3727
3728       xpos += player_xoffset;
3729       ypos += player_yoffset;
3730     }
3731   }
3732 }
3733
3734 void DrawPreviewLevelInitial(void)
3735 {
3736   DrawPreviewLevelExt(TRUE);
3737   DrawPreviewPlayers();
3738 }
3739
3740 void DrawPreviewLevelAnimation(void)
3741 {
3742   DrawPreviewLevelExt(FALSE);
3743 }
3744
3745 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3746                               int border_size, int font_nr)
3747 {
3748   int graphic = el2img(EL_PLAYER_1 + player_nr);
3749   int font_height = getFontHeight(font_nr);
3750   int player_height = MAX(tile_size, font_height);
3751   int xoffset_text = tile_size + border_size;
3752   int yoffset_text    = (player_height - font_height) / 2;
3753   int yoffset_graphic = (player_height - tile_size) / 2;
3754   char *player_name = getNetworkPlayerName(player_nr + 1);
3755
3756   DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3757                               tile_size);
3758   DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3759 }
3760
3761 static void DrawNetworkPlayersExt(boolean force)
3762 {
3763   if (game_status != GAME_MODE_MAIN)
3764     return;
3765
3766   if (!network.connected && !force)
3767     return;
3768
3769   // do not draw network players if level preview redefined, but players aren't
3770   if (preview.redefined && !menu.main.network_players.redefined)
3771     return;
3772
3773   int num_players = 0;
3774   int i;
3775
3776   for (i = 0; i < MAX_PLAYERS; i++)
3777     if (stored_player[i].connected_network)
3778       num_players++;
3779
3780   struct TextPosInfo *pos = &menu.main.network_players;
3781   int tile_size = pos->tile_size;
3782   int border_size = pos->border_size;
3783   int xoffset_text = tile_size + border_size;
3784   int font_nr = pos->font;
3785   int font_width = getFontWidth(font_nr);
3786   int font_height = getFontHeight(font_nr);
3787   int player_height = MAX(tile_size, font_height);
3788   int player_yoffset = player_height + border_size;
3789   int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3790   int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3791   int all_players_height = num_players * player_yoffset - border_size;
3792   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3793   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3794   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3795
3796   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3797                              max_players_width, max_players_height);
3798
3799   // first draw local network player ...
3800   for (i = 0; i < MAX_PLAYERS; i++)
3801   {
3802     if (stored_player[i].connected_network &&
3803         stored_player[i].connected_locally)
3804     {
3805       char *player_name = getNetworkPlayerName(i + 1);
3806       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3807       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3808
3809       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3810
3811       ypos += player_yoffset;
3812     }
3813   }
3814
3815   // ... then draw all other network players
3816   for (i = 0; i < MAX_PLAYERS; i++)
3817   {
3818     if (stored_player[i].connected_network &&
3819         !stored_player[i].connected_locally)
3820     {
3821       char *player_name = getNetworkPlayerName(i + 1);
3822       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3823       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3824
3825       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3826
3827       ypos += player_yoffset;
3828     }
3829   }
3830 }
3831
3832 void DrawNetworkPlayers(void)
3833 {
3834   DrawNetworkPlayersExt(FALSE);
3835 }
3836
3837 void ClearNetworkPlayers(void)
3838 {
3839   DrawNetworkPlayersExt(TRUE);
3840 }
3841
3842 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3843                                     int graphic, int sync_frame,
3844                                     int mask_mode)
3845 {
3846   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3847
3848   if (mask_mode == USE_MASKING)
3849     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3850   else
3851     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3852 }
3853
3854 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3855                                   int graphic, int sync_frame, int mask_mode)
3856 {
3857   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3858
3859   if (mask_mode == USE_MASKING)
3860     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3861   else
3862     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3863 }
3864
3865 static void DrawGraphicAnimation(int x, int y, int graphic)
3866 {
3867   int lx = LEVELX(x), ly = LEVELY(y);
3868   int mask_mode = NO_MASKING;
3869
3870   if (!IN_SCR_FIELD(x, y))
3871     return;
3872
3873   if (game.use_masked_elements)
3874   {
3875     if (Tile[lx][ly] != EL_EMPTY)
3876     {
3877       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3878
3879       mask_mode = USE_MASKING;
3880     }
3881   }
3882
3883   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3884                           graphic, GfxFrame[lx][ly], mask_mode);
3885
3886   MarkTileDirty(x, y);
3887 }
3888
3889 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3890 {
3891   int lx = LEVELX(x), ly = LEVELY(y);
3892   int mask_mode = NO_MASKING;
3893
3894   if (!IN_SCR_FIELD(x, y))
3895     return;
3896
3897   if (game.use_masked_elements)
3898   {
3899     if (Tile[lx][ly] != EL_EMPTY)
3900     {
3901       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3902
3903       mask_mode = USE_MASKING;
3904     }
3905   }
3906
3907   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3908                           graphic, GfxFrame[lx][ly], mask_mode);
3909
3910   MarkTileDirty(x, y);
3911 }
3912
3913 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3914 {
3915   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3916 }
3917
3918 void DrawLevelElementAnimation(int x, int y, int element)
3919 {
3920   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3921
3922   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3923 }
3924
3925 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3926 {
3927   int sx = SCREENX(x), sy = SCREENY(y);
3928
3929   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3930     return;
3931
3932   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3933     return;
3934
3935   DrawGraphicAnimation(sx, sy, graphic);
3936
3937 #if 1
3938   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3939     DrawLevelFieldCrumbled(x, y);
3940 #else
3941   if (GFX_CRUMBLED(Tile[x][y]))
3942     DrawLevelFieldCrumbled(x, y);
3943 #endif
3944 }
3945
3946 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3947 {
3948   int sx = SCREENX(x), sy = SCREENY(y);
3949   int graphic;
3950
3951   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3952     return;
3953
3954   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3955
3956   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3957     return;
3958
3959   DrawGraphicAnimation(sx, sy, graphic);
3960
3961   if (GFX_CRUMBLED(element))
3962     DrawLevelFieldCrumbled(x, y);
3963 }
3964
3965 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3966 {
3967   if (player->use_murphy)
3968   {
3969     // this works only because currently only one player can be "murphy" ...
3970     static int last_horizontal_dir = MV_LEFT;
3971     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3972
3973     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3974       last_horizontal_dir = move_dir;
3975
3976     if (graphic == IMG_SP_MURPHY)       // undefined => use special graphic
3977     {
3978       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3979
3980       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3981     }
3982
3983     return graphic;
3984   }
3985   else
3986     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3987 }
3988
3989 static boolean equalGraphics(int graphic1, int graphic2)
3990 {
3991   struct GraphicInfo *g1 = &graphic_info[graphic1];
3992   struct GraphicInfo *g2 = &graphic_info[graphic2];
3993
3994   return (g1->bitmap      == g2->bitmap &&
3995           g1->src_x       == g2->src_x &&
3996           g1->src_y       == g2->src_y &&
3997           g1->anim_frames == g2->anim_frames &&
3998           g1->anim_delay  == g2->anim_delay &&
3999           g1->anim_mode   == g2->anim_mode);
4000 }
4001
4002 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4003
4004 enum
4005 {
4006   DRAW_PLAYER_STAGE_INIT = 0,
4007   DRAW_PLAYER_STAGE_LAST_FIELD,
4008   DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4009 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4010   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4011   DRAW_PLAYER_STAGE_PLAYER,
4012 #else
4013   DRAW_PLAYER_STAGE_PLAYER,
4014   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4015 #endif
4016   DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4017   DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4018
4019   NUM_DRAW_PLAYER_STAGES
4020 };
4021
4022 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4023 {
4024   static int static_last_player_graphic[MAX_PLAYERS];
4025   static int static_last_player_frame[MAX_PLAYERS];
4026   static boolean static_player_is_opaque[MAX_PLAYERS];
4027   static boolean draw_player[MAX_PLAYERS];
4028   int pnr = player->index_nr;
4029
4030   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4031   {
4032     static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4033     static_last_player_frame[pnr] = player->Frame;
4034     static_player_is_opaque[pnr] = FALSE;
4035
4036     draw_player[pnr] = TRUE;
4037   }
4038
4039   if (!draw_player[pnr])
4040     return;
4041
4042 #if DEBUG
4043   if (!IN_LEV_FIELD(player->jx, player->jy))
4044   {
4045     Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4046     Debug("draw:DrawPlayerExt", "This should never happen!");
4047
4048     draw_player[pnr] = FALSE;
4049
4050     return;
4051   }
4052 #endif
4053
4054   int last_player_graphic  = static_last_player_graphic[pnr];
4055   int last_player_frame    = static_last_player_frame[pnr];
4056   boolean player_is_opaque = static_player_is_opaque[pnr];
4057
4058   int jx = player->jx;
4059   int jy = player->jy;
4060   int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4061   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4062   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
4063   int last_jx = (player->is_moving ? jx - dx : jx);
4064   int last_jy = (player->is_moving ? jy - dy : jy);
4065   int next_jx = jx + dx;
4066   int next_jy = jy + dy;
4067   boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4068   int sx = SCREENX(jx);
4069   int sy = SCREENY(jy);
4070   int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4071   int syy = (move_dir == MV_UP   || move_dir == MV_DOWN  ? player->GfxPos : 0);
4072   int element = Tile[jx][jy];
4073   int last_element = Tile[last_jx][last_jy];
4074   int action = (player->is_pushing    ? ACTION_PUSHING         :
4075                 player->is_digging    ? ACTION_DIGGING         :
4076                 player->is_collecting ? ACTION_COLLECTING      :
4077                 player->is_moving     ? ACTION_MOVING          :
4078                 player->is_snapping   ? ACTION_SNAPPING        :
4079                 player->is_dropping   ? ACTION_DROPPING        :
4080                 player->is_waiting    ? player->action_waiting :
4081                 ACTION_DEFAULT);
4082
4083   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4084   {
4085     // ------------------------------------------------------------------------
4086     // initialize drawing the player
4087     // ------------------------------------------------------------------------
4088
4089     draw_player[pnr] = FALSE;
4090
4091     // GfxElement[][] is set to the element the player is digging or collecting;
4092     // remove also for off-screen player if the player is not moving anymore
4093     if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4094       GfxElement[jx][jy] = EL_UNDEFINED;
4095
4096     if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4097       return;
4098
4099     if (element == EL_EXPLOSION)
4100       return;
4101
4102     InitPlayerGfxAnimation(player, action, move_dir);
4103
4104     draw_player[pnr] = TRUE;
4105   }
4106   else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4107   {
4108     // ------------------------------------------------------------------------
4109     // draw things in the field the player is leaving, if needed
4110     // ------------------------------------------------------------------------
4111
4112     if (!IN_SCR_FIELD(sx, sy))
4113       draw_player[pnr] = FALSE;
4114
4115     if (!player->is_moving)
4116       return;
4117
4118     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4119     {
4120       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4121
4122       if (last_element == EL_DYNAMITE_ACTIVE ||
4123           last_element == EL_EM_DYNAMITE_ACTIVE ||
4124           last_element == EL_SP_DISK_RED_ACTIVE)
4125         DrawDynamite(last_jx, last_jy);
4126       else
4127         DrawLevelFieldThruMask(last_jx, last_jy);
4128     }
4129     else if (last_element == EL_DYNAMITE_ACTIVE ||
4130              last_element == EL_EM_DYNAMITE_ACTIVE ||
4131              last_element == EL_SP_DISK_RED_ACTIVE)
4132       DrawDynamite(last_jx, last_jy);
4133     else
4134       DrawLevelField(last_jx, last_jy);
4135
4136     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4137       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4138   }
4139   else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4140   {
4141     // ------------------------------------------------------------------------
4142     // draw things behind the player, if needed
4143     // ------------------------------------------------------------------------
4144
4145     if (Back[jx][jy])
4146     {
4147       DrawLevelElement(jx, jy, Back[jx][jy]);
4148
4149       return;
4150     }
4151
4152     if (IS_ACTIVE_BOMB(element))
4153     {
4154       DrawLevelElement(jx, jy, EL_EMPTY);
4155
4156       return;
4157     }
4158
4159     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4160     {
4161       int old_element = GfxElement[jx][jy];
4162       int old_graphic = el_act_dir2img(old_element, action, move_dir);
4163       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4164
4165       if (GFX_CRUMBLED(old_element))
4166         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4167       else
4168         DrawScreenGraphic(sx, sy, old_graphic, frame);
4169
4170       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4171         static_player_is_opaque[pnr] = TRUE;
4172     }
4173     else
4174     {
4175       GfxElement[jx][jy] = EL_UNDEFINED;
4176
4177       // make sure that pushed elements are drawn with correct frame rate
4178       int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4179
4180       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4181         GfxFrame[jx][jy] = player->StepFrame;
4182
4183       DrawLevelField(jx, jy);
4184     }
4185   }
4186   else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4187   {
4188     // ------------------------------------------------------------------------
4189     // draw things the player is pushing, if needed
4190     // ------------------------------------------------------------------------
4191
4192     if (!player->is_pushing || !player->is_moving)
4193       return;
4194
4195     int gfx_frame = GfxFrame[jx][jy];
4196
4197     if (!IS_MOVING(jx, jy))             // push movement already finished
4198     {
4199       element = Tile[next_jx][next_jy];
4200       gfx_frame = GfxFrame[next_jx][next_jy];
4201     }
4202
4203     int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4204     int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4205     int frame = getGraphicAnimationFrame(graphic, sync_frame);
4206
4207     // draw background element under pushed element (like the Sokoban field)
4208     if (game.use_masked_pushing && IS_MOVING(jx, jy))
4209     {
4210       // this allows transparent pushing animation over non-black background
4211
4212       if (Back[jx][jy])
4213         DrawLevelElement(jx, jy, Back[jx][jy]);
4214       else
4215         DrawLevelElement(jx, jy, EL_EMPTY);
4216
4217       if (Back[next_jx][next_jy])
4218         DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4219       else
4220         DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4221     }
4222     else if (Back[next_jx][next_jy])
4223       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4224
4225     int px = SCREENX(jx), py = SCREENY(jy);
4226     int pxx = (TILEX - ABS(sxx)) * dx;
4227     int pyy = (TILEY - ABS(syy)) * dy;
4228
4229 #if 1
4230     // do not draw (EM style) pushing animation when pushing is finished
4231     // (two-tile animations usually do not contain start and end frame)
4232     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4233       DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4234     else
4235       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4236 #else
4237     // masked drawing is needed for EMC style (double) movement graphics
4238     // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4239     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4240 #endif
4241   }
4242   else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4243   {
4244     // ------------------------------------------------------------------------
4245     // draw player himself
4246     // ------------------------------------------------------------------------
4247
4248     int graphic = getPlayerGraphic(player, move_dir);
4249
4250     // in the case of changed player action or direction, prevent the current
4251     // animation frame from being restarted for identical animations
4252     if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4253       player->Frame = last_player_frame;
4254
4255     int frame = getGraphicAnimationFrame(graphic, player->Frame);
4256
4257     if (player_is_opaque)
4258       DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4259     else
4260       DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4261
4262     if (SHIELD_ON(player))
4263     {
4264       graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4265                  IMG_SHIELD_NORMAL_ACTIVE);
4266       frame = getGraphicAnimationFrame(graphic, -1);
4267
4268       DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4269     }
4270   }
4271   else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4272   {
4273     // ------------------------------------------------------------------------
4274     // draw things in front of player (active dynamite or dynabombs)
4275     // ------------------------------------------------------------------------
4276
4277     if (IS_ACTIVE_BOMB(element))
4278     {
4279       int graphic = el2img(element);
4280       int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4281
4282       if (game.emulation == EMU_SUPAPLEX)
4283         DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4284       else
4285         DrawGraphicThruMask(sx, sy, graphic, frame);
4286     }
4287
4288     if (player_is_moving && last_element == EL_EXPLOSION)
4289     {
4290       int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4291                      GfxElement[last_jx][last_jy] :  EL_EMPTY);
4292       int graphic = el_act2img(element, ACTION_EXPLODING);
4293       int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4294       int phase = ExplodePhase[last_jx][last_jy] - 1;
4295       int frame = getGraphicAnimationFrame(graphic, phase - delay);
4296
4297       if (phase >= delay)
4298         DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4299     }
4300   }
4301   else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4302   {
4303     // ------------------------------------------------------------------------
4304     // draw elements the player is just walking/passing through/under
4305     // ------------------------------------------------------------------------
4306
4307     if (player_is_moving)
4308     {
4309       // handle the field the player is leaving ...
4310       if (IS_ACCESSIBLE_INSIDE(last_element))
4311         DrawLevelField(last_jx, last_jy);
4312       else if (IS_ACCESSIBLE_UNDER(last_element))
4313         DrawLevelFieldThruMask(last_jx, last_jy);
4314     }
4315
4316     // do not redraw accessible elements if the player is just pushing them
4317     if (!player_is_moving || !player->is_pushing)
4318     {
4319       // ... and the field the player is entering
4320       if (IS_ACCESSIBLE_INSIDE(element))
4321         DrawLevelField(jx, jy);
4322       else if (IS_ACCESSIBLE_UNDER(element))
4323         DrawLevelFieldThruMask(jx, jy);
4324     }
4325
4326     MarkTileDirty(sx, sy);
4327   }
4328 }
4329
4330 void DrawPlayer(struct PlayerInfo *player)
4331 {
4332   int i;
4333
4334   for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4335     DrawPlayerExt(player, i);
4336 }
4337
4338 void DrawAllPlayers(void)
4339 {
4340   int i, j;
4341
4342   for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4343     for (j = 0; j < MAX_PLAYERS; j++)
4344       if (stored_player[j].active)
4345         DrawPlayerExt(&stored_player[j], i);
4346 }
4347
4348 void DrawPlayerField(int x, int y)
4349 {
4350   if (!IS_PLAYER(x, y))
4351     return;
4352
4353   DrawPlayer(PLAYERINFO(x, y));
4354 }
4355
4356 // ----------------------------------------------------------------------------
4357
4358 void WaitForEventToContinue(void)
4359 {
4360   boolean first_wait = TRUE;
4361   boolean still_wait = TRUE;
4362
4363   if (program.headless)
4364     return;
4365
4366   // simulate releasing mouse button over last gadget, if still pressed
4367   if (button_status)
4368     HandleGadgets(-1, -1, 0);
4369
4370   button_status = MB_RELEASED;
4371
4372   ClearEventQueue();
4373   ClearPlayerAction();
4374
4375   while (still_wait)
4376   {
4377     Event event;
4378
4379     if (NextValidEvent(&event))
4380     {
4381       switch (event.type)
4382       {
4383         case EVENT_BUTTONPRESS:
4384         case EVENT_FINGERPRESS:
4385           first_wait = FALSE;
4386           break;
4387
4388         case EVENT_BUTTONRELEASE:
4389         case EVENT_FINGERRELEASE:
4390           still_wait = first_wait;
4391           break;
4392
4393         case EVENT_KEYPRESS:
4394         case SDL_CONTROLLERBUTTONDOWN:
4395         case SDL_JOYBUTTONDOWN:
4396           still_wait = FALSE;
4397           break;
4398
4399         default:
4400           HandleOtherEvents(&event);
4401           break;
4402       }
4403     }
4404     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4405     {
4406       still_wait = FALSE;
4407     }
4408
4409     if (!PendingEvent())
4410       BackToFront();
4411   }
4412 }
4413
4414 #define MAX_REQUEST_LINES               13
4415 #define MAX_REQUEST_LINE_FONT1_LEN      7
4416 #define MAX_REQUEST_LINE_FONT2_LEN      10
4417
4418 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4419 {
4420   boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4421                              checkGameEnded());
4422   int draw_buffer_last = GetDrawtoField();
4423   int width  = request.width;
4424   int height = request.height;
4425   int sx, sy;
4426   int result;
4427
4428   // when showing request dialog after game ended, deactivate game panel
4429   if (game_just_ended)
4430     game.panel.active = FALSE;
4431
4432   game.request_active = TRUE;
4433
4434   setRequestPosition(&sx, &sy, FALSE);
4435
4436   button_status = MB_RELEASED;
4437
4438   request_gadget_id = -1;
4439   result = -1;
4440
4441   while (result < 0)
4442   {
4443     boolean event_handled = FALSE;
4444
4445     if (game_just_ended)
4446     {
4447       SetDrawtoField(draw_buffer_game);
4448
4449       HandleGameActions();
4450
4451       SetDrawtoField(DRAW_TO_BACKBUFFER);
4452
4453       if (global.use_envelope_request)
4454       {
4455         // copy current state of request area to middle of playfield area
4456         BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4457       }
4458     }
4459
4460     if (PendingEvent())
4461     {
4462       Event event;
4463
4464       while (NextValidEvent(&event))
4465       {
4466         event_handled = TRUE;
4467
4468         switch (event.type)
4469         {
4470           case EVENT_BUTTONPRESS:
4471           case EVENT_BUTTONRELEASE:
4472           case EVENT_MOTIONNOTIFY:
4473           {
4474             int mx, my;
4475
4476             if (event.type == EVENT_MOTIONNOTIFY)
4477             {
4478               if (!button_status)
4479                 continue;
4480
4481               motion_status = TRUE;
4482               mx = ((MotionEvent *) &event)->x;
4483               my = ((MotionEvent *) &event)->y;
4484             }
4485             else
4486             {
4487               motion_status = FALSE;
4488               mx = ((ButtonEvent *) &event)->x;
4489               my = ((ButtonEvent *) &event)->y;
4490               if (event.type == EVENT_BUTTONPRESS)
4491                 button_status = ((ButtonEvent *) &event)->button;
4492               else
4493                 button_status = MB_RELEASED;
4494             }
4495
4496             // this sets 'request_gadget_id'
4497             HandleGadgets(mx, my, button_status);
4498
4499             switch (request_gadget_id)
4500             {
4501               case TOOL_CTRL_ID_YES:
4502               case TOOL_CTRL_ID_TOUCH_YES:
4503                 result = TRUE;
4504                 break;
4505               case TOOL_CTRL_ID_NO:
4506               case TOOL_CTRL_ID_TOUCH_NO:
4507                 result = FALSE;
4508                 break;
4509               case TOOL_CTRL_ID_CONFIRM:
4510               case TOOL_CTRL_ID_TOUCH_CONFIRM:
4511                 result = TRUE | FALSE;
4512                 break;
4513
4514               case TOOL_CTRL_ID_PLAYER_1:
4515                 result = 1;
4516                 break;
4517               case TOOL_CTRL_ID_PLAYER_2:
4518                 result = 2;
4519                 break;
4520               case TOOL_CTRL_ID_PLAYER_3:
4521                 result = 3;
4522                 break;
4523               case TOOL_CTRL_ID_PLAYER_4:
4524                 result = 4;
4525                 break;
4526
4527               default:
4528                 // only check clickable animations if no request gadget clicked
4529                 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4530                 break;
4531             }
4532
4533             break;
4534           }
4535
4536           case SDL_WINDOWEVENT:
4537             HandleWindowEvent((WindowEvent *) &event);
4538             break;
4539
4540           case SDL_APP_WILLENTERBACKGROUND:
4541           case SDL_APP_DIDENTERBACKGROUND:
4542           case SDL_APP_WILLENTERFOREGROUND:
4543           case SDL_APP_DIDENTERFOREGROUND:
4544             HandlePauseResumeEvent((PauseResumeEvent *) &event);
4545             break;
4546
4547           case EVENT_KEYPRESS:
4548           {
4549             Key key = GetEventKey((KeyEvent *)&event, TRUE);
4550
4551             switch (key)
4552             {
4553               case KSYM_space:
4554                 if (req_state & REQ_CONFIRM)
4555                   result = 1;
4556                 break;
4557
4558               case KSYM_Return:
4559               case KSYM_y:
4560               case KSYM_Y:
4561               case KSYM_Select:
4562               case KSYM_Menu:
4563 #if defined(KSYM_Rewind)
4564               case KSYM_Rewind:         // for Amazon Fire TV remote
4565 #endif
4566                 result = 1;
4567                 break;
4568
4569               case KSYM_Escape:
4570               case KSYM_n:
4571               case KSYM_N:
4572               case KSYM_Back:
4573 #if defined(KSYM_FastForward)
4574               case KSYM_FastForward:    // for Amazon Fire TV remote
4575 #endif
4576                 result = 0;
4577                 break;
4578
4579               default:
4580                 HandleKeysDebug(key, KEY_PRESSED);
4581                 break;
4582             }
4583
4584             if (req_state & REQ_PLAYER)
4585             {
4586               int old_player_nr = setup.network_player_nr;
4587
4588               if (result != -1)
4589                 result = old_player_nr + 1;
4590
4591               switch (key)
4592               {
4593                 case KSYM_space:
4594                   result = old_player_nr + 1;
4595                   break;
4596
4597                 case KSYM_Up:
4598                 case KSYM_1:
4599                   result = 1;
4600                   break;
4601
4602                 case KSYM_Right:
4603                 case KSYM_2:
4604                   result = 2;
4605                   break;
4606
4607                 case KSYM_Down:
4608                 case KSYM_3:
4609                   result = 3;
4610                   break;
4611
4612                 case KSYM_Left:
4613                 case KSYM_4:
4614                   result = 4;
4615                   break;
4616
4617                 default:
4618                   break;
4619               }
4620             }
4621
4622             break;
4623           }
4624
4625           case EVENT_FINGERRELEASE:
4626           case EVENT_KEYRELEASE:
4627             ClearPlayerAction();
4628             break;
4629
4630           case SDL_CONTROLLERBUTTONDOWN:
4631             switch (event.cbutton.button)
4632             {
4633               case SDL_CONTROLLER_BUTTON_A:
4634               case SDL_CONTROLLER_BUTTON_X:
4635               case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4636               case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4637                 result = 1;
4638                 break;
4639
4640               case SDL_CONTROLLER_BUTTON_B:
4641               case SDL_CONTROLLER_BUTTON_Y:
4642               case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4643               case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4644               case SDL_CONTROLLER_BUTTON_BACK:
4645                 result = 0;
4646                 break;
4647             }
4648
4649             if (req_state & REQ_PLAYER)
4650             {
4651               int old_player_nr = setup.network_player_nr;
4652
4653               if (result != -1)
4654                 result = old_player_nr + 1;
4655
4656               switch (event.cbutton.button)
4657               {
4658                 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4659                 case SDL_CONTROLLER_BUTTON_Y:
4660                   result = 1;
4661                   break;
4662
4663                 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4664                 case SDL_CONTROLLER_BUTTON_B:
4665                   result = 2;
4666                   break;
4667
4668                 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4669                 case SDL_CONTROLLER_BUTTON_A:
4670                   result = 3;
4671                   break;
4672
4673                 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4674                 case SDL_CONTROLLER_BUTTON_X:
4675                   result = 4;
4676                   break;
4677
4678                 default:
4679                   break;
4680               }
4681             }
4682
4683             break;
4684
4685           case SDL_CONTROLLERBUTTONUP:
4686             HandleJoystickEvent(&event);
4687             ClearPlayerAction();
4688             break;
4689
4690           default:
4691             HandleOtherEvents(&event);
4692             break;
4693         }
4694       }
4695     }
4696     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4697     {
4698       int joy = AnyJoystick();
4699
4700       if (joy & JOY_BUTTON_1)
4701         result = 1;
4702       else if (joy & JOY_BUTTON_2)
4703         result = 0;
4704     }
4705     else if (AnyJoystick())
4706     {
4707       int joy = AnyJoystick();
4708
4709       if (req_state & REQ_PLAYER)
4710       {
4711         if (joy & JOY_UP)
4712           result = 1;
4713         else if (joy & JOY_RIGHT)
4714           result = 2;
4715         else if (joy & JOY_DOWN)
4716           result = 3;
4717         else if (joy & JOY_LEFT)
4718           result = 4;
4719       }
4720     }
4721
4722     if (event_handled)
4723     {
4724       if (game_just_ended)
4725       {
4726         if (global.use_envelope_request)
4727         {
4728           // copy back current state of pressed buttons inside request area
4729           BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4730         }
4731       }
4732
4733       PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4734     }
4735
4736     BackToFront();
4737   }
4738
4739   SetDrawtoField(draw_buffer_last);
4740
4741   game.request_active = FALSE;
4742
4743   return result;
4744 }
4745
4746 static boolean RequestDoor(char *text, unsigned int req_state)
4747 {
4748   int draw_buffer_last = GetDrawtoField();
4749   unsigned int old_door_state;
4750   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4751   int font_nr = FONT_TEXT_2;
4752   char *text_ptr;
4753   int result;
4754   int ty;
4755
4756   if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4757   {
4758     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4759     font_nr = FONT_TEXT_1;
4760   }
4761
4762   if (game_status == GAME_MODE_PLAYING)
4763     BlitScreenToBitmap(backbuffer);
4764
4765   // disable deactivated drawing when quick-loading level tape recording
4766   if (tape.playing && tape.deactivate_display)
4767     TapeDeactivateDisplayOff(TRUE);
4768
4769   SetMouseCursor(CURSOR_DEFAULT);
4770
4771   // pause network game while waiting for request to answer
4772   if (network.enabled &&
4773       game_status == GAME_MODE_PLAYING &&
4774       !game.all_players_gone &&
4775       req_state & REQUEST_WAIT_FOR_INPUT)
4776     SendToServer_PausePlaying();
4777
4778   old_door_state = GetDoorState();
4779
4780   // simulate releasing mouse button over last gadget, if still pressed
4781   if (button_status)
4782     HandleGadgets(-1, -1, 0);
4783
4784   UnmapAllGadgets();
4785
4786   // draw released gadget before proceeding
4787   // BackToFront();
4788
4789   if (old_door_state & DOOR_OPEN_1)
4790   {
4791     CloseDoor(DOOR_CLOSE_1);
4792
4793     // save old door content
4794     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4795                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4796   }
4797
4798   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4799   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4800
4801   // clear door drawing field
4802   DrawBackground(DX, DY, DXSIZE, DYSIZE);
4803
4804   // force DOOR font inside door area
4805   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4806
4807   // write text for request
4808   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4809   {
4810     char text_line[max_request_line_len + 1];
4811     int tx, tl, tc = 0;
4812
4813     if (!*text_ptr)
4814       break;
4815
4816     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4817     {
4818       tc = *(text_ptr + tx);
4819       // if (!tc || tc == ' ')
4820       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4821         break;
4822     }
4823
4824     if ((tc == '?' || tc == '!') && tl == 0)
4825       tl = 1;
4826
4827     if (!tl)
4828     { 
4829       text_ptr++; 
4830       ty--; 
4831       continue; 
4832     }
4833
4834     strncpy(text_line, text_ptr, tl);
4835     text_line[tl] = 0;
4836
4837     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4838              DY + 8 + ty * (getFontHeight(font_nr) + 2),
4839              text_line, font_nr);
4840
4841     text_ptr += tl + (tc == ' ' ? 1 : 0);
4842     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4843   }
4844
4845   ResetFontStatus();
4846
4847   if (req_state & REQ_ASK)
4848   {
4849     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4850     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4851     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4852     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4853   }
4854   else if (req_state & REQ_CONFIRM)
4855   {
4856     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4857     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4858   }
4859   else if (req_state & REQ_PLAYER)
4860   {
4861     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4862     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4863     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4864     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4865   }
4866
4867   // copy request gadgets to door backbuffer
4868   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4869
4870   OpenDoor(DOOR_OPEN_1);
4871
4872   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4873   {
4874     if (game_status == GAME_MODE_PLAYING)
4875     {
4876       SetPanelBackground();
4877       SetDrawBackgroundMask(REDRAW_DOOR_1);
4878     }
4879     else
4880     {
4881       SetDrawBackgroundMask(REDRAW_FIELD);
4882     }
4883
4884     return FALSE;
4885   }
4886
4887   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4888
4889   // ---------- handle request buttons ----------
4890   result = RequestHandleEvents(req_state, draw_buffer_last);
4891
4892   UnmapToolButtons();
4893
4894   if (!(req_state & REQ_STAY_OPEN))
4895   {
4896     CloseDoor(DOOR_CLOSE_1);
4897
4898     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4899         (req_state & REQ_REOPEN))
4900       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4901   }
4902
4903   RemapAllGadgets();
4904
4905   if (game_status == GAME_MODE_PLAYING)
4906   {
4907     SetPanelBackground();
4908     SetDrawBackgroundMask(REDRAW_DOOR_1);
4909   }
4910   else
4911   {
4912     SetDrawBackgroundMask(REDRAW_FIELD);
4913   }
4914
4915   // continue network game after request
4916   if (network.enabled &&
4917       game_status == GAME_MODE_PLAYING &&
4918       !game.all_players_gone &&
4919       req_state & REQUEST_WAIT_FOR_INPUT)
4920     SendToServer_ContinuePlaying();
4921
4922   // restore deactivated drawing when quick-loading level tape recording
4923   if (tape.playing && tape.deactivate_display)
4924     TapeDeactivateDisplayOn();
4925
4926   return result;
4927 }
4928
4929 static boolean RequestEnvelope(char *text, unsigned int req_state)
4930 {
4931   int draw_buffer_last = GetDrawtoField();
4932   int result;
4933
4934   if (game_status == GAME_MODE_PLAYING)
4935     BlitScreenToBitmap(backbuffer);
4936
4937   // disable deactivated drawing when quick-loading level tape recording
4938   if (tape.playing && tape.deactivate_display)
4939     TapeDeactivateDisplayOff(TRUE);
4940
4941   SetMouseCursor(CURSOR_DEFAULT);
4942
4943   // pause network game while waiting for request to answer
4944   if (network.enabled &&
4945       game_status == GAME_MODE_PLAYING &&
4946       !game.all_players_gone &&
4947       req_state & REQUEST_WAIT_FOR_INPUT)
4948     SendToServer_PausePlaying();
4949
4950   // simulate releasing mouse button over last gadget, if still pressed
4951   if (button_status)
4952     HandleGadgets(-1, -1, 0);
4953
4954   UnmapAllGadgets();
4955
4956   // (replace with setting corresponding request background)
4957   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4958   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4959
4960   // clear door drawing field
4961   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4962
4963   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4964
4965   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4966   {
4967     if (game_status == GAME_MODE_PLAYING)
4968     {
4969       SetPanelBackground();
4970       SetDrawBackgroundMask(REDRAW_DOOR_1);
4971     }
4972     else
4973     {
4974       SetDrawBackgroundMask(REDRAW_FIELD);
4975     }
4976
4977     return FALSE;
4978   }
4979
4980   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4981
4982   // ---------- handle request buttons ----------
4983   result = RequestHandleEvents(req_state, draw_buffer_last);
4984
4985   UnmapToolButtons();
4986
4987   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4988
4989   RemapAllGadgets();
4990
4991   if (game_status == GAME_MODE_PLAYING)
4992   {
4993     SetPanelBackground();
4994     SetDrawBackgroundMask(REDRAW_DOOR_1);
4995   }
4996   else
4997   {
4998     SetDrawBackgroundMask(REDRAW_FIELD);
4999   }
5000
5001   // continue network game after request
5002   if (network.enabled &&
5003       game_status == GAME_MODE_PLAYING &&
5004       !game.all_players_gone &&
5005       req_state & REQUEST_WAIT_FOR_INPUT)
5006     SendToServer_ContinuePlaying();
5007
5008   // restore deactivated drawing when quick-loading level tape recording
5009   if (tape.playing && tape.deactivate_display)
5010     TapeDeactivateDisplayOn();
5011
5012   return result;
5013 }
5014
5015 boolean Request(char *text, unsigned int req_state)
5016 {
5017   boolean overlay_enabled = GetOverlayEnabled();
5018   boolean result;
5019
5020   game.request_active_or_moving = TRUE;
5021
5022   SetOverlayEnabled(FALSE);
5023
5024   if (global.use_envelope_request)
5025     result = RequestEnvelope(text, req_state);
5026   else
5027     result = RequestDoor(text, req_state);
5028
5029   SetOverlayEnabled(overlay_enabled);
5030
5031   game.request_active_or_moving = FALSE;
5032
5033   return result;
5034 }
5035
5036 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5037 {
5038   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5039   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5040   int compare_result;
5041
5042   if (dpo1->sort_priority != dpo2->sort_priority)
5043     compare_result = dpo1->sort_priority - dpo2->sort_priority;
5044   else
5045     compare_result = dpo1->nr - dpo2->nr;
5046
5047   return compare_result;
5048 }
5049
5050 void InitGraphicCompatibilityInfo_Doors(void)
5051 {
5052   struct
5053   {
5054     int door_token;
5055     int part_1, part_8;
5056     struct DoorInfo *door;
5057   }
5058   doors[] =
5059   {
5060     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
5061     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
5062
5063     { -1,       -1,                     -1,                     NULL    }
5064   };
5065   struct Rect door_rect_list[] =
5066   {
5067     { DX, DY, DXSIZE, DYSIZE },
5068     { VX, VY, VXSIZE, VYSIZE }
5069   };
5070   int i, j;
5071
5072   for (i = 0; doors[i].door_token != -1; i++)
5073   {
5074     int door_token = doors[i].door_token;
5075     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5076     int part_1 = doors[i].part_1;
5077     int part_8 = doors[i].part_8;
5078     int part_2 = part_1 + 1;
5079     int part_3 = part_1 + 2;
5080     struct DoorInfo *door = doors[i].door;
5081     struct Rect *door_rect = &door_rect_list[door_index];
5082     boolean door_gfx_redefined = FALSE;
5083
5084     // check if any door part graphic definitions have been redefined
5085
5086     for (j = 0; door_part_controls[j].door_token != -1; j++)
5087     {
5088       struct DoorPartControlInfo *dpc = &door_part_controls[j];
5089       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5090
5091       if (dpc->door_token == door_token && fi->redefined)
5092         door_gfx_redefined = TRUE;
5093     }
5094
5095     // check for old-style door graphic/animation modifications
5096
5097     if (!door_gfx_redefined)
5098     {
5099       if (door->anim_mode & ANIM_STATIC_PANEL)
5100       {
5101         door->panel.step_xoffset = 0;
5102         door->panel.step_yoffset = 0;
5103       }
5104
5105       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5106       {
5107         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5108         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5109         int num_door_steps, num_panel_steps;
5110
5111         // remove door part graphics other than the two default wings
5112
5113         for (j = 0; door_part_controls[j].door_token != -1; j++)
5114         {
5115           struct DoorPartControlInfo *dpc = &door_part_controls[j];
5116           struct GraphicInfo *g = &graphic_info[dpc->graphic];
5117
5118           if (dpc->graphic >= part_3 &&
5119               dpc->graphic <= part_8)
5120             g->bitmap = NULL;
5121         }
5122
5123         // set graphics and screen positions of the default wings
5124
5125         g_part_1->width  = door_rect->width;
5126         g_part_1->height = door_rect->height;
5127         g_part_2->width  = door_rect->width;
5128         g_part_2->height = door_rect->height;
5129         g_part_2->src_x = door_rect->width;
5130         g_part_2->src_y = g_part_1->src_y;
5131
5132         door->part_2.x = door->part_1.x;
5133         door->part_2.y = door->part_1.y;
5134
5135         if (door->width != -1)
5136         {
5137           g_part_1->width = door->width;
5138           g_part_2->width = door->width;
5139
5140           // special treatment for graphics and screen position of right wing
5141           g_part_2->src_x += door_rect->width - door->width;
5142           door->part_2.x  += door_rect->width - door->width;
5143         }
5144
5145         if (door->height != -1)
5146         {
5147           g_part_1->height = door->height;
5148           g_part_2->height = door->height;
5149
5150           // special treatment for graphics and screen position of bottom wing
5151           g_part_2->src_y += door_rect->height - door->height;
5152           door->part_2.y  += door_rect->height - door->height;
5153         }
5154
5155         // set animation delays for the default wings and panels
5156
5157         door->part_1.step_delay = door->step_delay;
5158         door->part_2.step_delay = door->step_delay;
5159         door->panel.step_delay  = door->step_delay;
5160
5161         // set animation draw order for the default wings
5162
5163         door->part_1.sort_priority = 2; // draw left wing over ...
5164         door->part_2.sort_priority = 1; //          ... right wing
5165
5166         // set animation draw offset for the default wings
5167
5168         if (door->anim_mode & ANIM_HORIZONTAL)
5169         {
5170           door->part_1.step_xoffset = door->step_offset;
5171           door->part_1.step_yoffset = 0;
5172           door->part_2.step_xoffset = door->step_offset * -1;
5173           door->part_2.step_yoffset = 0;
5174
5175           num_door_steps = g_part_1->width / door->step_offset;
5176         }
5177         else    // ANIM_VERTICAL
5178         {
5179           door->part_1.step_xoffset = 0;
5180           door->part_1.step_yoffset = door->step_offset;
5181           door->part_2.step_xoffset = 0;
5182           door->part_2.step_yoffset = door->step_offset * -1;
5183
5184           num_door_steps = g_part_1->height / door->step_offset;
5185         }
5186
5187         // set animation draw offset for the default panels
5188
5189         if (door->step_offset > 1)
5190         {
5191           num_panel_steps = 2 * door_rect->height / door->step_offset;
5192           door->panel.start_step = num_panel_steps - num_door_steps;
5193           door->panel.start_step_closing = door->panel.start_step;
5194         }
5195         else
5196         {
5197           num_panel_steps = door_rect->height / door->step_offset;
5198           door->panel.start_step = num_panel_steps - num_door_steps / 2;
5199           door->panel.start_step_closing = door->panel.start_step;
5200           door->panel.step_delay *= 2;
5201         }
5202       }
5203     }
5204   }
5205 }
5206
5207 void InitDoors(void)
5208 {
5209   int i;
5210
5211   for (i = 0; door_part_controls[i].door_token != -1; i++)
5212   {
5213     struct DoorPartControlInfo *dpc = &door_part_controls[i];
5214     struct DoorPartOrderInfo *dpo = &door_part_order[i];
5215
5216     // initialize "start_step_opening" and "start_step_closing", if needed
5217     if (dpc->pos->start_step_opening == 0 &&
5218         dpc->pos->start_step_closing == 0)
5219     {
5220       // dpc->pos->start_step_opening = dpc->pos->start_step;
5221       dpc->pos->start_step_closing = dpc->pos->start_step;
5222     }
5223
5224     // fill structure for door part draw order (sorted below)
5225     dpo->nr = i;
5226     dpo->sort_priority = dpc->pos->sort_priority;
5227   }
5228
5229   // sort door part controls according to sort_priority and graphic number
5230   qsort(door_part_order, MAX_DOOR_PARTS,
5231         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5232 }
5233
5234 unsigned int OpenDoor(unsigned int door_state)
5235 {
5236   if (door_state & DOOR_COPY_BACK)
5237   {
5238     if (door_state & DOOR_OPEN_1)
5239       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5240                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5241
5242     if (door_state & DOOR_OPEN_2)
5243       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5244                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5245
5246     door_state &= ~DOOR_COPY_BACK;
5247   }
5248
5249   return MoveDoor(door_state);
5250 }
5251
5252 unsigned int CloseDoor(unsigned int door_state)
5253 {
5254   unsigned int old_door_state = GetDoorState();
5255
5256   if (!(door_state & DOOR_NO_COPY_BACK))
5257   {
5258     if (old_door_state & DOOR_OPEN_1)
5259       BlitBitmap(backbuffer, bitmap_db_door_1,
5260                  DX, DY, DXSIZE, DYSIZE, 0, 0);
5261
5262     if (old_door_state & DOOR_OPEN_2)
5263       BlitBitmap(backbuffer, bitmap_db_door_2,
5264                  VX, VY, VXSIZE, VYSIZE, 0, 0);
5265
5266     door_state &= ~DOOR_NO_COPY_BACK;
5267   }
5268
5269   return MoveDoor(door_state);
5270 }
5271
5272 unsigned int GetDoorState(void)
5273 {
5274   return MoveDoor(DOOR_GET_STATE);
5275 }
5276
5277 unsigned int SetDoorState(unsigned int door_state)
5278 {
5279   return MoveDoor(door_state | DOOR_SET_STATE);
5280 }
5281
5282 static int euclid(int a, int b)
5283 {
5284   return (b ? euclid(b, a % b) : a);
5285 }
5286
5287 unsigned int MoveDoor(unsigned int door_state)
5288 {
5289   struct Rect door_rect_list[] =
5290   {
5291     { DX, DY, DXSIZE, DYSIZE },
5292     { VX, VY, VXSIZE, VYSIZE }
5293   };
5294   static int door1 = DOOR_CLOSE_1;
5295   static int door2 = DOOR_CLOSE_2;
5296   unsigned int door_delay = 0;
5297   unsigned int door_delay_value;
5298   int i;
5299
5300   if (door_state == DOOR_GET_STATE)
5301     return (door1 | door2);
5302
5303   if (door_state & DOOR_SET_STATE)
5304   {
5305     if (door_state & DOOR_ACTION_1)
5306       door1 = door_state & DOOR_ACTION_1;
5307     if (door_state & DOOR_ACTION_2)
5308       door2 = door_state & DOOR_ACTION_2;
5309
5310     return (door1 | door2);
5311   }
5312
5313   if (!(door_state & DOOR_FORCE_REDRAW))
5314   {
5315     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5316       door_state &= ~DOOR_OPEN_1;
5317     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5318       door_state &= ~DOOR_CLOSE_1;
5319     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5320       door_state &= ~DOOR_OPEN_2;
5321     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5322       door_state &= ~DOOR_CLOSE_2;
5323   }
5324
5325   if (global.autoplay_leveldir)
5326   {
5327     door_state |= DOOR_NO_DELAY;
5328     door_state &= ~DOOR_CLOSE_ALL;
5329   }
5330
5331   if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5332     door_state |= DOOR_NO_DELAY;
5333
5334   if (door_state & DOOR_ACTION)
5335   {
5336     boolean door_panel_drawn[NUM_DOORS];
5337     boolean panel_has_doors[NUM_DOORS];
5338     boolean door_part_skip[MAX_DOOR_PARTS];
5339     boolean door_part_done[MAX_DOOR_PARTS];
5340     boolean door_part_done_all;
5341     int num_steps[MAX_DOOR_PARTS];
5342     int max_move_delay = 0;     // delay for complete animations of all doors
5343     int max_step_delay = 0;     // delay (ms) between two animation frames
5344     int num_move_steps = 0;     // number of animation steps for all doors
5345     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
5346     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
5347     int current_move_delay = 0;
5348     int start = 0;
5349     int k;
5350
5351     for (i = 0; i < NUM_DOORS; i++)
5352       panel_has_doors[i] = FALSE;
5353
5354     for (i = 0; i < MAX_DOOR_PARTS; i++)
5355     {
5356       struct DoorPartControlInfo *dpc = &door_part_controls[i];
5357       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5358       int door_token = dpc->door_token;
5359
5360       door_part_done[i] = FALSE;
5361       door_part_skip[i] = (!(door_state & door_token) ||
5362                            !g->bitmap);
5363     }
5364
5365     for (i = 0; i < MAX_DOOR_PARTS; i++)
5366     {
5367       int nr = door_part_order[i].nr;
5368       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5369       struct DoorPartPosInfo *pos = dpc->pos;
5370       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5371       int door_token = dpc->door_token;
5372       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5373       boolean is_panel = DOOR_PART_IS_PANEL(nr);
5374       int step_xoffset = ABS(pos->step_xoffset);
5375       int step_yoffset = ABS(pos->step_yoffset);
5376       int step_delay = pos->step_delay;
5377       int current_door_state = door_state & door_token;
5378       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5379       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5380       boolean part_opening = (is_panel ? door_closing : door_opening);
5381       int start_step = (part_opening ? pos->start_step_opening :
5382                         pos->start_step_closing);
5383       float move_xsize = (step_xoffset ? g->width  : 0);
5384       float move_ysize = (step_yoffset ? g->height : 0);
5385       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5386       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5387       int move_steps = (move_xsteps && move_ysteps ?
5388                         MIN(move_xsteps, move_ysteps) :
5389                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
5390       int move_delay = move_steps * step_delay;
5391
5392       if (door_part_skip[nr])
5393         continue;
5394
5395       max_move_delay = MAX(max_move_delay, move_delay);
5396       max_step_delay = (max_step_delay == 0 ? step_delay :
5397                         euclid(max_step_delay, step_delay));
5398       num_steps[nr] = move_steps;
5399
5400       if (!is_panel)
5401       {
5402         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5403
5404         panel_has_doors[door_index] = TRUE;
5405       }
5406     }
5407
5408     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
5409
5410     num_move_steps = max_move_delay / max_step_delay;
5411     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5412
5413     door_delay_value = max_step_delay;
5414
5415     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5416     {
5417       start = num_move_steps - 1;
5418     }
5419     else
5420     {
5421       // opening door sound has priority over simultaneously closing door
5422       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5423       {
5424         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5425
5426         if (door_state & DOOR_OPEN_1)
5427           PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5428         if (door_state & DOOR_OPEN_2)
5429           PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5430       }
5431       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5432       {
5433         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5434
5435         if (door_state & DOOR_CLOSE_1)
5436           PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5437         if (door_state & DOOR_CLOSE_2)
5438           PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5439       }
5440     }
5441
5442     for (k = start; k < num_move_steps; k++)
5443     {
5444       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
5445
5446       door_part_done_all = TRUE;
5447
5448       for (i = 0; i < NUM_DOORS; i++)
5449         door_panel_drawn[i] = FALSE;
5450
5451       for (i = 0; i < MAX_DOOR_PARTS; i++)
5452       {
5453         int nr = door_part_order[i].nr;
5454         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5455         struct DoorPartPosInfo *pos = dpc->pos;
5456         struct GraphicInfo *g = &graphic_info[dpc->graphic];
5457         int door_token = dpc->door_token;
5458         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5459         boolean is_panel = DOOR_PART_IS_PANEL(nr);
5460         boolean is_panel_and_door_has_closed = FALSE;
5461         struct Rect *door_rect = &door_rect_list[door_index];
5462         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5463                                   bitmap_db_door_2);
5464         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5465         int current_door_state = door_state & door_token;
5466         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5467         boolean door_closing = !door_opening;
5468         boolean part_opening = (is_panel ? door_closing : door_opening);
5469         boolean part_closing = !part_opening;
5470         int start_step = (part_opening ? pos->start_step_opening :
5471                           pos->start_step_closing);
5472         int step_delay = pos->step_delay;
5473         int step_factor = step_delay / max_step_delay;
5474         int k1 = (step_factor ? k / step_factor + 1 : k);
5475         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5476         int kk = MAX(0, k2);
5477         int g_src_x = 0;
5478         int g_src_y = 0;
5479         int src_x, src_y, src_xx, src_yy;
5480         int dst_x, dst_y, dst_xx, dst_yy;
5481         int width, height;
5482
5483         if (door_part_skip[nr])
5484           continue;
5485
5486         if (!(door_state & door_token))
5487           continue;
5488
5489         if (!g->bitmap)
5490           continue;
5491
5492         if (!is_panel)
5493         {
5494           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5495           int kk_door = MAX(0, k2_door);
5496           int sync_frame = kk_door * door_delay_value;
5497           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5498
5499           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5500                                 &g_src_x, &g_src_y);
5501         }
5502
5503         // draw door panel
5504
5505         if (!door_panel_drawn[door_index])
5506         {
5507           ClearRectangle(drawto, door_rect->x, door_rect->y,
5508                          door_rect->width, door_rect->height);
5509
5510           door_panel_drawn[door_index] = TRUE;
5511         }
5512
5513         // draw opening or closing door parts
5514
5515         if (pos->step_xoffset < 0)      // door part on right side
5516         {
5517           src_xx = 0;
5518           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5519           width = g->width;
5520
5521           if (dst_xx + width > door_rect->width)
5522             width = door_rect->width - dst_xx;
5523         }
5524         else                            // door part on left side
5525         {
5526           src_xx = 0;
5527           dst_xx = pos->x - kk * pos->step_xoffset;
5528
5529           if (dst_xx < 0)
5530           {
5531             src_xx = ABS(dst_xx);
5532             dst_xx = 0;
5533           }
5534
5535           width = g->width - src_xx;
5536
5537           if (width > door_rect->width)
5538             width = door_rect->width;
5539
5540           // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5541         }
5542
5543         if (pos->step_yoffset < 0)      // door part on bottom side
5544         {
5545           src_yy = 0;
5546           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5547           height = g->height;
5548
5549           if (dst_yy + height > door_rect->height)
5550             height = door_rect->height - dst_yy;
5551         }
5552         else                            // door part on top side
5553         {
5554           src_yy = 0;
5555           dst_yy = pos->y - kk * pos->step_yoffset;
5556
5557           if (dst_yy < 0)
5558           {
5559             src_yy = ABS(dst_yy);
5560             dst_yy = 0;
5561           }
5562
5563           height = g->height - src_yy;
5564         }
5565
5566         src_x = g_src_x + src_xx;
5567         src_y = g_src_y + src_yy;
5568
5569         dst_x = door_rect->x + dst_xx;
5570         dst_y = door_rect->y + dst_yy;
5571
5572         is_panel_and_door_has_closed =
5573           (is_panel &&
5574            door_closing &&
5575            panel_has_doors[door_index] &&
5576            k >= num_move_steps_doors_only - 1);
5577
5578         if (width  >= 0 && width  <= g->width &&
5579             height >= 0 && height <= g->height &&
5580             !is_panel_and_door_has_closed)
5581         {
5582           if (is_panel || !pos->draw_masked)
5583             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5584                        dst_x, dst_y);
5585           else
5586             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5587                              dst_x, dst_y);
5588         }
5589
5590         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5591
5592         if ((part_opening && (width < 0         || height < 0)) ||
5593             (part_closing && (width >= g->width && height >= g->height)))
5594           door_part_done[nr] = TRUE;
5595
5596         // continue door part animations, but not panel after door has closed
5597         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5598           door_part_done_all = FALSE;
5599       }
5600
5601       if (!(door_state & DOOR_NO_DELAY))
5602       {
5603         BackToFront();
5604
5605         SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5606
5607         current_move_delay += max_step_delay;
5608
5609         // prevent OS (Windows) from complaining about program not responding
5610         CheckQuitEvent();
5611       }
5612
5613       if (door_part_done_all)
5614         break;
5615     }
5616
5617     if (!(door_state & DOOR_NO_DELAY))
5618     {
5619       // wait for specified door action post delay
5620       if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5621         door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5622       else if (door_state & DOOR_ACTION_1)
5623         door_delay_value = door_1.post_delay;
5624       else if (door_state & DOOR_ACTION_2)
5625         door_delay_value = door_2.post_delay;
5626
5627       while (!DelayReached(&door_delay, door_delay_value))
5628         BackToFront();
5629     }
5630   }
5631
5632   if (door_state & DOOR_ACTION_1)
5633     door1 = door_state & DOOR_ACTION_1;
5634   if (door_state & DOOR_ACTION_2)
5635     door2 = door_state & DOOR_ACTION_2;
5636
5637   // draw masked border over door area
5638   DrawMaskedBorder(REDRAW_DOOR_1);
5639   DrawMaskedBorder(REDRAW_DOOR_2);
5640
5641   ClearAutoRepeatKeyEvents();
5642
5643   return (door1 | door2);
5644 }
5645
5646 static boolean useSpecialEditorDoor(void)
5647 {
5648   int graphic = IMG_GLOBAL_BORDER_EDITOR;
5649   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5650
5651   // do not draw special editor door if editor border defined or redefined
5652   if (graphic_info[graphic].bitmap != NULL || redefined)
5653     return FALSE;
5654
5655   // do not draw special editor door if global border defined to be empty
5656   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5657     return FALSE;
5658
5659   // do not draw special editor door if viewport definitions do not match
5660   if (EX != VX ||
5661       EY >= VY ||
5662       EXSIZE != VXSIZE ||
5663       EY + EYSIZE != VY + VYSIZE)
5664     return FALSE;
5665
5666   return TRUE;
5667 }
5668
5669 void DrawSpecialEditorDoor(void)
5670 {
5671   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5672   int top_border_width = gfx1->width;
5673   int top_border_height = gfx1->height;
5674   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5675   int ex = EX - outer_border;
5676   int ey = EY - outer_border;
5677   int vy = VY - outer_border;
5678   int exsize = EXSIZE + 2 * outer_border;
5679
5680   if (!useSpecialEditorDoor())
5681     return;
5682
5683   // draw bigger level editor toolbox window
5684   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5685              top_border_width, top_border_height, ex, ey - top_border_height);
5686   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5687              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5688
5689   redraw_mask |= REDRAW_ALL;
5690 }
5691
5692 void UndrawSpecialEditorDoor(void)
5693 {
5694   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5695   int top_border_width = gfx1->width;
5696   int top_border_height = gfx1->height;
5697   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5698   int ex = EX - outer_border;
5699   int ey = EY - outer_border;
5700   int ey_top = ey - top_border_height;
5701   int exsize = EXSIZE + 2 * outer_border;
5702   int eysize = EYSIZE + 2 * outer_border;
5703
5704   if (!useSpecialEditorDoor())
5705     return;
5706
5707   // draw normal tape recorder window
5708   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5709   {
5710     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5711                ex, ey_top, top_border_width, top_border_height,
5712                ex, ey_top);
5713     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5714                ex, ey, exsize, eysize, ex, ey);
5715   }
5716   else
5717   {
5718     // if screen background is set to "[NONE]", clear editor toolbox window
5719     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5720     ClearRectangle(drawto, ex, ey, exsize, eysize);
5721   }
5722
5723   redraw_mask |= REDRAW_ALL;
5724 }
5725
5726
5727 // ---------- new tool button stuff -------------------------------------------
5728
5729 static struct
5730 {
5731   int graphic;
5732   struct TextPosInfo *pos;
5733   int gadget_id;
5734   boolean is_touch_button;
5735   char *infotext;
5736 } toolbutton_info[NUM_TOOL_BUTTONS] =
5737 {
5738   {
5739     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
5740     TOOL_CTRL_ID_YES, FALSE,            "yes"
5741   },
5742   {
5743     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5744     TOOL_CTRL_ID_NO, FALSE,             "no"
5745   },
5746   {
5747     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5748     TOOL_CTRL_ID_CONFIRM, FALSE,        "confirm"
5749   },
5750   {
5751     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5752     TOOL_CTRL_ID_PLAYER_1, FALSE,       "player 1"
5753   },
5754   {
5755     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5756     TOOL_CTRL_ID_PLAYER_2, FALSE,       "player 2"
5757   },
5758   {
5759     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5760     TOOL_CTRL_ID_PLAYER_3, FALSE,       "player 3"
5761   },
5762   {
5763     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5764     TOOL_CTRL_ID_PLAYER_4, FALSE,       "player 4"
5765   },
5766   {
5767     IMG_GFX_REQUEST_BUTTON_TOUCH_YES,   &request.button.touch_yes,
5768     TOOL_CTRL_ID_TOUCH_YES, TRUE,       "yes"
5769   },
5770   {
5771     IMG_GFX_REQUEST_BUTTON_TOUCH_NO,    &request.button.touch_no,
5772     TOOL_CTRL_ID_TOUCH_NO, TRUE,        "no"
5773   },
5774   {
5775     IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5776     TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE,   "confirm"
5777   }
5778 };
5779
5780 void CreateToolButtons(void)
5781 {
5782   int i;
5783
5784   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5785   {
5786     int graphic = toolbutton_info[i].graphic;
5787     struct GraphicInfo *gfx = &graphic_info[graphic];
5788     struct TextPosInfo *pos = toolbutton_info[i].pos;
5789     struct GadgetInfo *gi;
5790     Bitmap *deco_bitmap = None;
5791     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5792     unsigned int event_mask = GD_EVENT_RELEASED;
5793     boolean is_touch_button = toolbutton_info[i].is_touch_button;
5794     int base_x = (is_touch_button ? 0 : DX);
5795     int base_y = (is_touch_button ? 0 : DY);
5796     int gd_x = gfx->src_x;
5797     int gd_y = gfx->src_y;
5798     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5799     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5800     int x = pos->x;
5801     int y = pos->y;
5802     int id = i;
5803
5804     if (global.use_envelope_request && !is_touch_button)
5805     {
5806       setRequestPosition(&base_x, &base_y, TRUE);
5807
5808       // check if request buttons are outside of envelope and fix, if needed
5809       if (x < 0 || x + gfx->width  > request.width ||
5810           y < 0 || y + gfx->height > request.height)
5811       {
5812         if (id == TOOL_CTRL_ID_YES)
5813         {
5814           x = 0;
5815           y = request.height - 2 * request.border_size - gfx->height;
5816         }
5817         else if (id == TOOL_CTRL_ID_NO)
5818         {
5819           x = request.width  - 2 * request.border_size - gfx->width;
5820           y = request.height - 2 * request.border_size - gfx->height;
5821         }
5822         else if (id == TOOL_CTRL_ID_CONFIRM)
5823         {
5824           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5825           y = request.height - 2 * request.border_size - gfx->height;
5826         }
5827         else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5828         {
5829           int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5830
5831           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5832           y = request.height - 2 * request.border_size - gfx->height * 2;
5833
5834           x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5835           y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5836         }
5837       }
5838     }
5839
5840     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5841     {
5842       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5843
5844       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5845                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5846       deco_xpos = (gfx->width  - pos->size) / 2;
5847       deco_ypos = (gfx->height - pos->size) / 2;
5848     }
5849
5850     gi = CreateGadget(GDI_CUSTOM_ID, id,
5851                       GDI_IMAGE_ID, graphic,
5852                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5853                       GDI_X, base_x + x,
5854                       GDI_Y, base_y + y,
5855                       GDI_WIDTH, gfx->width,
5856                       GDI_HEIGHT, gfx->height,
5857                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5858                       GDI_STATE, GD_BUTTON_UNPRESSED,
5859                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5860                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5861                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5862                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5863                       GDI_DECORATION_SIZE, pos->size, pos->size,
5864                       GDI_DECORATION_SHIFTING, 1, 1,
5865                       GDI_DIRECT_DRAW, FALSE,
5866                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5867                       GDI_EVENT_MASK, event_mask,
5868                       GDI_CALLBACK_ACTION, HandleToolButtons,
5869                       GDI_END);
5870
5871     if (gi == NULL)
5872       Fail("cannot create gadget");
5873
5874     tool_gadget[id] = gi;
5875   }
5876 }
5877
5878 void FreeToolButtons(void)
5879 {
5880   int i;
5881
5882   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5883     FreeGadget(tool_gadget[i]);
5884 }
5885
5886 static void UnmapToolButtons(void)
5887 {
5888   int i;
5889
5890   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5891     UnmapGadget(tool_gadget[i]);
5892 }
5893
5894 static void HandleToolButtons(struct GadgetInfo *gi)
5895 {
5896   request_gadget_id = gi->custom_id;
5897 }
5898
5899 static struct Mapping_EM_to_RND_object
5900 {
5901   int element_em;
5902   boolean is_rnd_to_em_mapping;         // unique mapping EM <-> RND
5903   boolean is_backside;                  // backside of moving element
5904
5905   int element_rnd;
5906   int action;
5907   int direction;
5908 }
5909 em_object_mapping_list[GAME_TILE_MAX + 1] =
5910 {
5911   {
5912     Zborder,                            FALSE,  FALSE,
5913     EL_EMPTY,                           -1, -1
5914   },
5915   {
5916     Zplayer,                            FALSE,  FALSE,
5917     EL_EMPTY,                           -1, -1
5918   },
5919
5920   {
5921     Zbug,                               FALSE,  FALSE,
5922     EL_EMPTY,                           -1, -1
5923   },
5924   {
5925     Ztank,                              FALSE,  FALSE,
5926     EL_EMPTY,                           -1, -1
5927   },
5928   {
5929     Zeater,                             FALSE,  FALSE,
5930     EL_EMPTY,                           -1, -1
5931   },
5932   {
5933     Zdynamite,                          FALSE,  FALSE,
5934     EL_EMPTY,                           -1, -1
5935   },
5936   {
5937     Zboom,                              FALSE,  FALSE,
5938     EL_EMPTY,                           -1, -1
5939   },
5940
5941   {
5942     Xchain,                             FALSE,  FALSE,
5943     EL_DEFAULT,                         ACTION_EXPLODING, -1
5944   },
5945   {
5946     Xboom_bug,                          FALSE,  FALSE,
5947     EL_BUG,                             ACTION_EXPLODING, -1
5948   },
5949   {
5950     Xboom_tank,                         FALSE,  FALSE,
5951     EL_SPACESHIP,                       ACTION_EXPLODING, -1
5952   },
5953   {
5954     Xboom_android,                      FALSE,  FALSE,
5955     EL_EMC_ANDROID,                     ACTION_OTHER, -1
5956   },
5957   {
5958     Xboom_1,                            FALSE,  FALSE,
5959     EL_DEFAULT,                         ACTION_EXPLODING, -1
5960   },
5961   {
5962     Xboom_2,                            FALSE,  FALSE,
5963     EL_DEFAULT,                         ACTION_EXPLODING, -1
5964   },
5965
5966   {
5967     Xblank,                             TRUE,   FALSE,
5968     EL_EMPTY,                           -1, -1
5969   },
5970
5971   {
5972     Xsplash_e,                          FALSE,  FALSE,
5973     EL_ACID_SPLASH_RIGHT,               -1, -1
5974   },
5975   {
5976     Xsplash_w,                          FALSE,  FALSE,
5977     EL_ACID_SPLASH_LEFT,                -1, -1
5978   },
5979
5980   {
5981     Xplant,                             TRUE,   FALSE,
5982     EL_EMC_PLANT,                       -1, -1
5983   },
5984   {
5985     Yplant,                             FALSE,  FALSE,
5986     EL_EMC_PLANT,                       -1, -1
5987   },
5988
5989   {
5990     Xacid_1,                            TRUE,   FALSE,
5991     EL_ACID,                            -1, -1
5992   },
5993   {
5994     Xacid_2,                            FALSE,  FALSE,
5995     EL_ACID,                            -1, -1
5996   },
5997   {
5998     Xacid_3,                            FALSE,  FALSE,
5999     EL_ACID,                            -1, -1
6000   },
6001   {
6002     Xacid_4,                            FALSE,  FALSE,
6003     EL_ACID,                            -1, -1
6004   },
6005   {
6006     Xacid_5,                            FALSE,  FALSE,
6007     EL_ACID,                            -1, -1
6008   },
6009   {
6010     Xacid_6,                            FALSE,  FALSE,
6011     EL_ACID,                            -1, -1
6012   },
6013   {
6014     Xacid_7,                            FALSE,  FALSE,
6015     EL_ACID,                            -1, -1
6016   },
6017   {
6018     Xacid_8,                            FALSE,  FALSE,
6019     EL_ACID,                            -1, -1
6020   },
6021
6022   {
6023     Xfake_acid_1,                       TRUE,   FALSE,
6024     EL_EMC_FAKE_ACID,                   -1, -1
6025   },
6026   {
6027     Xfake_acid_2,                       FALSE,  FALSE,
6028     EL_EMC_FAKE_ACID,                   -1, -1
6029   },
6030   {
6031     Xfake_acid_3,                       FALSE,  FALSE,
6032     EL_EMC_FAKE_ACID,                   -1, -1
6033   },
6034   {
6035     Xfake_acid_4,                       FALSE,  FALSE,
6036     EL_EMC_FAKE_ACID,                   -1, -1
6037   },
6038   {
6039     Xfake_acid_5,                       FALSE,  FALSE,
6040     EL_EMC_FAKE_ACID,                   -1, -1
6041   },
6042   {
6043     Xfake_acid_6,                       FALSE,  FALSE,
6044     EL_EMC_FAKE_ACID,                   -1, -1
6045   },
6046   {
6047     Xfake_acid_7,                       FALSE,  FALSE,
6048     EL_EMC_FAKE_ACID,                   -1, -1
6049   },
6050   {
6051     Xfake_acid_8,                       FALSE,  FALSE,
6052     EL_EMC_FAKE_ACID,                   -1, -1
6053   },
6054
6055   {
6056     Xfake_acid_1_player,                FALSE,  FALSE,
6057     EL_EMC_FAKE_ACID,                   -1, -1
6058   },
6059   {
6060     Xfake_acid_2_player,                FALSE,  FALSE,
6061     EL_EMC_FAKE_ACID,                   -1, -1
6062   },
6063   {
6064     Xfake_acid_3_player,                FALSE,  FALSE,
6065     EL_EMC_FAKE_ACID,                   -1, -1
6066   },
6067   {
6068     Xfake_acid_4_player,                FALSE,  FALSE,
6069     EL_EMC_FAKE_ACID,                   -1, -1
6070   },
6071   {
6072     Xfake_acid_5_player,                FALSE,  FALSE,
6073     EL_EMC_FAKE_ACID,                   -1, -1
6074   },
6075   {
6076     Xfake_acid_6_player,                FALSE,  FALSE,
6077     EL_EMC_FAKE_ACID,                   -1, -1
6078   },
6079   {
6080     Xfake_acid_7_player,                FALSE,  FALSE,
6081     EL_EMC_FAKE_ACID,                   -1, -1
6082   },
6083   {
6084     Xfake_acid_8_player,                FALSE,  FALSE,
6085     EL_EMC_FAKE_ACID,                   -1, -1
6086   },
6087
6088   {
6089     Xgrass,                             TRUE,   FALSE,
6090     EL_EMC_GRASS,                       -1, -1
6091   },
6092   {
6093     Ygrass_nB,                          FALSE,  FALSE,
6094     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
6095   },
6096   {
6097     Ygrass_eB,                          FALSE,  FALSE,
6098     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
6099   },
6100   {
6101     Ygrass_sB,                          FALSE,  FALSE,
6102     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
6103   },
6104   {
6105     Ygrass_wB,                          FALSE,  FALSE,
6106     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
6107   },
6108
6109   {
6110     Xdirt,                              TRUE,   FALSE,
6111     EL_SAND,                            -1, -1
6112   },
6113   {
6114     Ydirt_nB,                           FALSE,  FALSE,
6115     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
6116   },
6117   {
6118     Ydirt_eB,                           FALSE,  FALSE,
6119     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
6120   },
6121   {
6122     Ydirt_sB,                           FALSE,  FALSE,
6123     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
6124   },
6125   {
6126     Ydirt_wB,                           FALSE,  FALSE,
6127     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
6128   },
6129
6130   {
6131     Xandroid,                           TRUE,   FALSE,
6132     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
6133   },
6134   {
6135     Xandroid_1_n,                       FALSE,  FALSE,
6136     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6137   },
6138   {
6139     Xandroid_2_n,                       FALSE,  FALSE,
6140     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6141   },
6142   {
6143     Xandroid_1_e,                       FALSE,  FALSE,
6144     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6145   },
6146   {
6147     Xandroid_2_e,                       FALSE,  FALSE,
6148     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6149   },
6150   {
6151     Xandroid_1_w,                       FALSE,  FALSE,
6152     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6153   },
6154   {
6155     Xandroid_2_w,                       FALSE,  FALSE,
6156     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6157   },
6158   {
6159     Xandroid_1_s,                       FALSE,  FALSE,
6160     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6161   },
6162   {
6163     Xandroid_2_s,                       FALSE,  FALSE,
6164     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6165   },
6166   {
6167     Yandroid_n,                         FALSE,  FALSE,
6168     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6169   },
6170   {
6171     Yandroid_nB,                        FALSE,  TRUE,
6172     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6173   },
6174   {
6175     Yandroid_ne,                        FALSE,  FALSE,
6176     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
6177   },
6178   {
6179     Yandroid_neB,                       FALSE,  TRUE,
6180     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
6181   },
6182   {
6183     Yandroid_e,                         FALSE,  FALSE,
6184     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6185   },
6186   {
6187     Yandroid_eB,                        FALSE,  TRUE,
6188     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6189   },
6190   {
6191     Yandroid_se,                        FALSE,  FALSE,
6192     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
6193   },
6194   {
6195     Yandroid_seB,                       FALSE,  TRUE,
6196     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6197   },
6198   {
6199     Yandroid_s,                         FALSE,  FALSE,
6200     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6201   },
6202   {
6203     Yandroid_sB,                        FALSE,  TRUE,
6204     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6205   },
6206   {
6207     Yandroid_sw,                        FALSE,  FALSE,
6208     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
6209   },
6210   {
6211     Yandroid_swB,                       FALSE,  TRUE,
6212     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
6213   },
6214   {
6215     Yandroid_w,                         FALSE,  FALSE,
6216     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6217   },
6218   {
6219     Yandroid_wB,                        FALSE,  TRUE,
6220     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6221   },
6222   {
6223     Yandroid_nw,                        FALSE,  FALSE,
6224     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
6225   },
6226   {
6227     Yandroid_nwB,                       FALSE,  TRUE,
6228     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
6229   },
6230
6231   {
6232     Xeater_n,                           TRUE,   FALSE,
6233     EL_YAMYAM_UP,                       -1, -1
6234   },
6235   {
6236     Xeater_e,                           TRUE,   FALSE,
6237     EL_YAMYAM_RIGHT,                    -1, -1
6238   },
6239   {
6240     Xeater_w,                           TRUE,   FALSE,
6241     EL_YAMYAM_LEFT,                     -1, -1
6242   },
6243   {
6244     Xeater_s,                           TRUE,   FALSE,
6245     EL_YAMYAM_DOWN,                     -1, -1
6246   },
6247   {
6248     Yeater_n,                           FALSE,  FALSE,
6249     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6250   },
6251   {
6252     Yeater_nB,                          FALSE,  TRUE,
6253     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6254   },
6255   {
6256     Yeater_e,                           FALSE,  FALSE,
6257     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6258   },
6259   {
6260     Yeater_eB,                          FALSE,  TRUE,
6261     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6262   },
6263   {
6264     Yeater_s,                           FALSE,  FALSE,
6265     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6266   },
6267   {
6268     Yeater_sB,                          FALSE,  TRUE,
6269     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6270   },
6271   {
6272     Yeater_w,                           FALSE,  FALSE,
6273     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6274   },
6275   {
6276     Yeater_wB,                          FALSE,  TRUE,
6277     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6278   },
6279   {
6280     Yeater_stone,                       FALSE,  FALSE,
6281     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
6282   },
6283   {
6284     Yeater_spring,                      FALSE,  FALSE,
6285     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
6286   },
6287
6288   {
6289     Xalien,                             TRUE,   FALSE,
6290     EL_ROBOT,                           -1, -1
6291   },
6292   {
6293     Xalien_pause,                       FALSE,  FALSE,
6294     EL_ROBOT,                           -1, -1
6295   },
6296   {
6297     Yalien_n,                           FALSE,  FALSE,
6298     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6299   },
6300   {
6301     Yalien_nB,                          FALSE,  TRUE,
6302     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6303   },
6304   {
6305     Yalien_e,                           FALSE,  FALSE,
6306     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6307   },
6308   {
6309     Yalien_eB,                          FALSE,  TRUE,
6310     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6311   },
6312   {
6313     Yalien_s,                           FALSE,  FALSE,
6314     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6315   },
6316   {
6317     Yalien_sB,                          FALSE,  TRUE,
6318     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6319   },
6320   {
6321     Yalien_w,                           FALSE,  FALSE,
6322     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6323   },
6324   {
6325     Yalien_wB,                          FALSE,  TRUE,
6326     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6327   },
6328   {
6329     Yalien_stone,                       FALSE,  FALSE,
6330     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
6331   },
6332   {
6333     Yalien_spring,                      FALSE,  FALSE,
6334     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
6335   },
6336
6337   {
6338     Xbug_1_n,                           TRUE,   FALSE,
6339     EL_BUG_UP,                          -1, -1
6340   },
6341   {
6342     Xbug_1_e,                           TRUE,   FALSE,
6343     EL_BUG_RIGHT,                       -1, -1
6344   },
6345   {
6346     Xbug_1_s,                           TRUE,   FALSE,
6347     EL_BUG_DOWN,                        -1, -1
6348   },
6349   {
6350     Xbug_1_w,                           TRUE,   FALSE,
6351     EL_BUG_LEFT,                        -1, -1
6352   },
6353   {
6354     Xbug_2_n,                           FALSE,  FALSE,
6355     EL_BUG_UP,                          -1, -1
6356   },
6357   {
6358     Xbug_2_e,                           FALSE,  FALSE,
6359     EL_BUG_RIGHT,                       -1, -1
6360   },
6361   {
6362     Xbug_2_s,                           FALSE,  FALSE,
6363     EL_BUG_DOWN,                        -1, -1
6364   },
6365   {
6366     Xbug_2_w,                           FALSE,  FALSE,
6367     EL_BUG_LEFT,                        -1, -1
6368   },
6369   {
6370     Ybug_n,                             FALSE,  FALSE,
6371     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6372   },
6373   {
6374     Ybug_nB,                            FALSE,  TRUE,
6375     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6376   },
6377   {
6378     Ybug_e,                             FALSE,  FALSE,
6379     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6380   },
6381   {
6382     Ybug_eB,                            FALSE,  TRUE,
6383     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6384   },
6385   {
6386     Ybug_s,                             FALSE,  FALSE,
6387     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6388   },
6389   {
6390     Ybug_sB,                            FALSE,  TRUE,
6391     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6392   },
6393   {
6394     Ybug_w,                             FALSE,  FALSE,
6395     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6396   },
6397   {
6398     Ybug_wB,                            FALSE,  TRUE,
6399     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6400   },
6401   {
6402     Ybug_w_n,                           FALSE,  FALSE,
6403     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6404   },
6405   {
6406     Ybug_n_e,                           FALSE,  FALSE,
6407     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6408   },
6409   {
6410     Ybug_e_s,                           FALSE,  FALSE,
6411     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6412   },
6413   {
6414     Ybug_s_w,                           FALSE,  FALSE,
6415     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6416   },
6417   {
6418     Ybug_e_n,                           FALSE,  FALSE,
6419     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6420   },
6421   {
6422     Ybug_s_e,                           FALSE,  FALSE,
6423     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6424   },
6425   {
6426     Ybug_w_s,                           FALSE,  FALSE,
6427     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6428   },
6429   {
6430     Ybug_n_w,                           FALSE,  FALSE,
6431     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6432   },
6433   {
6434     Ybug_stone,                         FALSE,  FALSE,
6435     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
6436   },
6437   {
6438     Ybug_spring,                        FALSE,  FALSE,
6439     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
6440   },
6441
6442   {
6443     Xtank_1_n,                          TRUE,   FALSE,
6444     EL_SPACESHIP_UP,                    -1, -1
6445   },
6446   {
6447     Xtank_1_e,                          TRUE,   FALSE,
6448     EL_SPACESHIP_RIGHT,                 -1, -1
6449   },
6450   {
6451     Xtank_1_s,                          TRUE,   FALSE,
6452     EL_SPACESHIP_DOWN,                  -1, -1
6453   },
6454   {
6455     Xtank_1_w,                          TRUE,   FALSE,
6456     EL_SPACESHIP_LEFT,                  -1, -1
6457   },
6458   {
6459     Xtank_2_n,                          FALSE,  FALSE,
6460     EL_SPACESHIP_UP,                    -1, -1
6461   },
6462   {
6463     Xtank_2_e,                          FALSE,  FALSE,
6464     EL_SPACESHIP_RIGHT,                 -1, -1
6465   },
6466   {
6467     Xtank_2_s,                          FALSE,  FALSE,
6468     EL_SPACESHIP_DOWN,                  -1, -1
6469   },
6470   {
6471     Xtank_2_w,                          FALSE,  FALSE,
6472     EL_SPACESHIP_LEFT,                  -1, -1
6473   },
6474   {
6475     Ytank_n,                            FALSE,  FALSE,
6476     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6477   },
6478   {
6479     Ytank_nB,                           FALSE,  TRUE,
6480     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6481   },
6482   {
6483     Ytank_e,                            FALSE,  FALSE,
6484     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6485   },
6486   {
6487     Ytank_eB,                           FALSE,  TRUE,
6488     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6489   },
6490   {
6491     Ytank_s,                            FALSE,  FALSE,
6492     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6493   },
6494   {
6495     Ytank_sB,                           FALSE,  TRUE,
6496     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6497   },
6498   {
6499     Ytank_w,                            FALSE,  FALSE,
6500     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6501   },
6502   {
6503     Ytank_wB,                           FALSE,  TRUE,
6504     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6505   },
6506   {
6507     Ytank_w_n,                          FALSE,  FALSE,
6508     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6509   },
6510   {
6511     Ytank_n_e,                          FALSE,  FALSE,
6512     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6513   },
6514   {
6515     Ytank_e_s,                          FALSE,  FALSE,
6516     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6517   },
6518   {
6519     Ytank_s_w,                          FALSE,  FALSE,
6520     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6521   },
6522   {
6523     Ytank_e_n,                          FALSE,  FALSE,
6524     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6525   },
6526   {
6527     Ytank_s_e,                          FALSE,  FALSE,
6528     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6529   },
6530   {
6531     Ytank_w_s,                          FALSE,  FALSE,
6532     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6533   },
6534   {
6535     Ytank_n_w,                          FALSE,  FALSE,
6536     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6537   },
6538   {
6539     Ytank_stone,                        FALSE,  FALSE,
6540     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
6541   },
6542   {
6543     Ytank_spring,                       FALSE,  FALSE,
6544     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
6545   },
6546
6547   {
6548     Xemerald,                           TRUE,   FALSE,
6549     EL_EMERALD,                         -1, -1
6550   },
6551   {
6552     Xemerald_pause,                     FALSE,  FALSE,
6553     EL_EMERALD,                         -1, -1
6554   },
6555   {
6556     Xemerald_fall,                      FALSE,  FALSE,
6557     EL_EMERALD,                         -1, -1
6558   },
6559   {
6560     Xemerald_shine,                     FALSE,  FALSE,
6561     EL_EMERALD,                         ACTION_TWINKLING, -1
6562   },
6563   {
6564     Yemerald_s,                         FALSE,  FALSE,
6565     EL_EMERALD,                         ACTION_FALLING, -1
6566   },
6567   {
6568     Yemerald_sB,                        FALSE,  TRUE,
6569     EL_EMERALD,                         ACTION_FALLING, -1
6570   },
6571   {
6572     Yemerald_e,                         FALSE,  FALSE,
6573     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6574   },
6575   {
6576     Yemerald_eB,                        FALSE,  TRUE,
6577     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6578   },
6579   {
6580     Yemerald_w,                         FALSE,  FALSE,
6581     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6582   },
6583   {
6584     Yemerald_wB,                        FALSE,  TRUE,
6585     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6586   },
6587   {
6588     Yemerald_blank,                     FALSE,  FALSE,
6589     EL_EMERALD,                         ACTION_COLLECTING, -1
6590   },
6591
6592   {
6593     Xdiamond,                           TRUE,   FALSE,
6594     EL_DIAMOND,                         -1, -1
6595   },
6596   {
6597     Xdiamond_pause,                     FALSE,  FALSE,
6598     EL_DIAMOND,                         -1, -1
6599   },
6600   {
6601     Xdiamond_fall,                      FALSE,  FALSE,
6602     EL_DIAMOND,                         -1, -1
6603   },
6604   {
6605     Xdiamond_shine,                     FALSE,  FALSE,
6606     EL_DIAMOND,                         ACTION_TWINKLING, -1
6607   },
6608   {
6609     Ydiamond_s,                         FALSE,  FALSE,
6610     EL_DIAMOND,                         ACTION_FALLING, -1
6611   },
6612   {
6613     Ydiamond_sB,                        FALSE,  TRUE,
6614     EL_DIAMOND,                         ACTION_FALLING, -1
6615   },
6616   {
6617     Ydiamond_e,                         FALSE,  FALSE,
6618     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6619   },
6620   {
6621     Ydiamond_eB,                        FALSE,  TRUE,
6622     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6623   },
6624   {
6625     Ydiamond_w,                         FALSE,  FALSE,
6626     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6627   },
6628   {
6629     Ydiamond_wB,                        FALSE,  TRUE,
6630     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6631   },
6632   {
6633     Ydiamond_blank,                     FALSE,  FALSE,
6634     EL_DIAMOND,                         ACTION_COLLECTING, -1
6635   },
6636   {
6637     Ydiamond_stone,                     FALSE,  FALSE,
6638     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
6639   },
6640
6641   {
6642     Xstone,                             TRUE,   FALSE,
6643     EL_ROCK,                            -1, -1
6644   },
6645   {
6646     Xstone_pause,                       FALSE,  FALSE,
6647     EL_ROCK,                            -1, -1
6648   },
6649   {
6650     Xstone_fall,                        FALSE,  FALSE,
6651     EL_ROCK,                            -1, -1
6652   },
6653   {
6654     Ystone_s,                           FALSE,  FALSE,
6655     EL_ROCK,                            ACTION_FALLING, -1
6656   },
6657   {
6658     Ystone_sB,                          FALSE,  TRUE,
6659     EL_ROCK,                            ACTION_FALLING, -1
6660   },
6661   {
6662     Ystone_e,                           FALSE,  FALSE,
6663     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6664   },
6665   {
6666     Ystone_eB,                          FALSE,  TRUE,
6667     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6668   },
6669   {
6670     Ystone_w,                           FALSE,  FALSE,
6671     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6672   },
6673   {
6674     Ystone_wB,                          FALSE,  TRUE,
6675     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6676   },
6677
6678   {
6679     Xbomb,                              TRUE,   FALSE,
6680     EL_BOMB,                            -1, -1
6681   },
6682   {
6683     Xbomb_pause,                        FALSE,  FALSE,
6684     EL_BOMB,                            -1, -1
6685   },
6686   {
6687     Xbomb_fall,                         FALSE,  FALSE,
6688     EL_BOMB,                            -1, -1
6689   },
6690   {
6691     Ybomb_s,                            FALSE,  FALSE,
6692     EL_BOMB,                            ACTION_FALLING, -1
6693   },
6694   {
6695     Ybomb_sB,                           FALSE,  TRUE,
6696     EL_BOMB,                            ACTION_FALLING, -1
6697   },
6698   {
6699     Ybomb_e,                            FALSE,  FALSE,
6700     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6701   },
6702   {
6703     Ybomb_eB,                           FALSE,  TRUE,
6704     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6705   },
6706   {
6707     Ybomb_w,                            FALSE,  FALSE,
6708     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6709   },
6710   {
6711     Ybomb_wB,                           FALSE,  TRUE,
6712     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6713   },
6714   {
6715     Ybomb_blank,                        FALSE,  FALSE,
6716     EL_BOMB,                            ACTION_ACTIVATING, -1
6717   },
6718
6719   {
6720     Xnut,                               TRUE,   FALSE,
6721     EL_NUT,                             -1, -1
6722   },
6723   {
6724     Xnut_pause,                         FALSE,  FALSE,
6725     EL_NUT,                             -1, -1
6726   },
6727   {
6728     Xnut_fall,                          FALSE,  FALSE,
6729     EL_NUT,                             -1, -1
6730   },
6731   {
6732     Ynut_s,                             FALSE,  FALSE,
6733     EL_NUT,                             ACTION_FALLING, -1
6734   },
6735   {
6736     Ynut_sB,                            FALSE,  TRUE,
6737     EL_NUT,                             ACTION_FALLING, -1
6738   },
6739   {
6740     Ynut_e,                             FALSE,  FALSE,
6741     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6742   },
6743   {
6744     Ynut_eB,                            FALSE,  TRUE,
6745     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6746   },
6747   {
6748     Ynut_w,                             FALSE,  FALSE,
6749     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6750   },
6751   {
6752     Ynut_wB,                            FALSE,  TRUE,
6753     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6754   },
6755   {
6756     Ynut_stone,                         FALSE,  FALSE,
6757     EL_NUT,                             ACTION_BREAKING, -1
6758   },
6759
6760   {
6761     Xspring,                            TRUE,   FALSE,
6762     EL_SPRING,                          -1, -1
6763   },
6764   {
6765     Xspring_pause,                      FALSE,  FALSE,
6766     EL_SPRING,                          -1, -1
6767   },
6768   {
6769     Xspring_e,                          TRUE,   FALSE,
6770     EL_SPRING_RIGHT,                    -1, -1
6771   },
6772   {
6773     Xspring_w,                          TRUE,   FALSE,
6774     EL_SPRING_LEFT,                     -1, -1
6775   },
6776   {
6777     Xspring_fall,                       FALSE,  FALSE,
6778     EL_SPRING,                          -1, -1
6779   },
6780   {
6781     Yspring_s,                          FALSE,  FALSE,
6782     EL_SPRING,                          ACTION_FALLING, -1
6783   },
6784   {
6785     Yspring_sB,                         FALSE,  TRUE,
6786     EL_SPRING,                          ACTION_FALLING, -1
6787   },
6788   {
6789     Yspring_e,                          FALSE,  FALSE,
6790     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6791   },
6792   {
6793     Yspring_eB,                         FALSE,  TRUE,
6794     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6795   },
6796   {
6797     Yspring_w,                          FALSE,  FALSE,
6798     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6799   },
6800   {
6801     Yspring_wB,                         FALSE,  TRUE,
6802     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6803   },
6804   {
6805     Yspring_alien_e,                    FALSE,  FALSE,
6806     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6807   },
6808   {
6809     Yspring_alien_eB,                   FALSE,  TRUE,
6810     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6811   },
6812   {
6813     Yspring_alien_w,                    FALSE,  FALSE,
6814     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6815   },
6816   {
6817     Yspring_alien_wB,                   FALSE,  TRUE,
6818     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6819   },
6820
6821   {
6822     Xpush_emerald_e,                    FALSE,  FALSE,
6823     EL_EMERALD,                         -1, MV_BIT_RIGHT
6824   },
6825   {
6826     Xpush_emerald_w,                    FALSE,  FALSE,
6827     EL_EMERALD,                         -1, MV_BIT_LEFT
6828   },
6829   {
6830     Xpush_diamond_e,                    FALSE,  FALSE,
6831     EL_DIAMOND,                         -1, MV_BIT_RIGHT
6832   },
6833   {
6834     Xpush_diamond_w,                    FALSE,  FALSE,
6835     EL_DIAMOND,                         -1, MV_BIT_LEFT
6836   },
6837   {
6838     Xpush_stone_e,                      FALSE,  FALSE,
6839     EL_ROCK,                            -1, MV_BIT_RIGHT
6840   },
6841   {
6842     Xpush_stone_w,                      FALSE,  FALSE,
6843     EL_ROCK,                            -1, MV_BIT_LEFT
6844   },
6845   {
6846     Xpush_bomb_e,                       FALSE,  FALSE,
6847     EL_BOMB,                            -1, MV_BIT_RIGHT
6848   },
6849   {
6850     Xpush_bomb_w,                       FALSE,  FALSE,
6851     EL_BOMB,                            -1, MV_BIT_LEFT
6852   },
6853   {
6854     Xpush_nut_e,                        FALSE,  FALSE,
6855     EL_NUT,                             -1, MV_BIT_RIGHT
6856   },
6857   {
6858     Xpush_nut_w,                        FALSE,  FALSE,
6859     EL_NUT,                             -1, MV_BIT_LEFT
6860   },
6861   {
6862     Xpush_spring_e,                     FALSE,  FALSE,
6863     EL_SPRING_RIGHT,                    -1, MV_BIT_RIGHT
6864   },
6865   {
6866     Xpush_spring_w,                     FALSE,  FALSE,
6867     EL_SPRING_LEFT,                     -1, MV_BIT_LEFT
6868   },
6869
6870   {
6871     Xdynamite,                          TRUE,   FALSE,
6872     EL_EM_DYNAMITE,                     -1, -1
6873   },
6874   {
6875     Ydynamite_blank,                    FALSE,  FALSE,
6876     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6877   },
6878   {
6879     Xdynamite_1,                        TRUE,   FALSE,
6880     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6881   },
6882   {
6883     Xdynamite_2,                        FALSE,  FALSE,
6884     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6885   },
6886   {
6887     Xdynamite_3,                        FALSE,  FALSE,
6888     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6889   },
6890   {
6891     Xdynamite_4,                        FALSE,  FALSE,
6892     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6893   },
6894
6895   {
6896     Xkey_1,                             TRUE,   FALSE,
6897     EL_EM_KEY_1,                        -1, -1
6898   },
6899   {
6900     Xkey_2,                             TRUE,   FALSE,
6901     EL_EM_KEY_2,                        -1, -1
6902   },
6903   {
6904     Xkey_3,                             TRUE,   FALSE,
6905     EL_EM_KEY_3,                        -1, -1
6906   },
6907   {
6908     Xkey_4,                             TRUE,   FALSE,
6909     EL_EM_KEY_4,                        -1, -1
6910   },
6911   {
6912     Xkey_5,                             TRUE,   FALSE,
6913     EL_EMC_KEY_5,                       -1, -1
6914   },
6915   {
6916     Xkey_6,                             TRUE,   FALSE,
6917     EL_EMC_KEY_6,                       -1, -1
6918   },
6919   {
6920     Xkey_7,                             TRUE,   FALSE,
6921     EL_EMC_KEY_7,                       -1, -1
6922   },
6923   {
6924     Xkey_8,                             TRUE,   FALSE,
6925     EL_EMC_KEY_8,                       -1, -1
6926   },
6927
6928   {
6929     Xdoor_1,                            TRUE,   FALSE,
6930     EL_EM_GATE_1,                       -1, -1
6931   },
6932   {
6933     Xdoor_2,                            TRUE,   FALSE,
6934     EL_EM_GATE_2,                       -1, -1
6935   },
6936   {
6937     Xdoor_3,                            TRUE,   FALSE,
6938     EL_EM_GATE_3,                       -1, -1
6939   },
6940   {
6941     Xdoor_4,                            TRUE,   FALSE,
6942     EL_EM_GATE_4,                       -1, -1
6943   },
6944   {
6945     Xdoor_5,                            TRUE,   FALSE,
6946     EL_EMC_GATE_5,                      -1, -1
6947   },
6948   {
6949     Xdoor_6,                            TRUE,   FALSE,
6950     EL_EMC_GATE_6,                      -1, -1
6951   },
6952   {
6953     Xdoor_7,                            TRUE,   FALSE,
6954     EL_EMC_GATE_7,                      -1, -1
6955   },
6956   {
6957     Xdoor_8,                            TRUE,   FALSE,
6958     EL_EMC_GATE_8,                      -1, -1
6959   },
6960
6961   {
6962     Xfake_door_1,                       TRUE,   FALSE,
6963     EL_EM_GATE_1_GRAY,                  -1, -1
6964   },
6965   {
6966     Xfake_door_2,                       TRUE,   FALSE,
6967     EL_EM_GATE_2_GRAY,                  -1, -1
6968   },
6969   {
6970     Xfake_door_3,                       TRUE,   FALSE,
6971     EL_EM_GATE_3_GRAY,                  -1, -1
6972   },
6973   {
6974     Xfake_door_4,                       TRUE,   FALSE,
6975     EL_EM_GATE_4_GRAY,                  -1, -1
6976   },
6977   {
6978     Xfake_door_5,                       TRUE,   FALSE,
6979     EL_EMC_GATE_5_GRAY,                 -1, -1
6980   },
6981   {
6982     Xfake_door_6,                       TRUE,   FALSE,
6983     EL_EMC_GATE_6_GRAY,                 -1, -1
6984   },
6985   {
6986     Xfake_door_7,                       TRUE,   FALSE,
6987     EL_EMC_GATE_7_GRAY,                 -1, -1
6988   },
6989   {
6990     Xfake_door_8,                       TRUE,   FALSE,
6991     EL_EMC_GATE_8_GRAY,                 -1, -1
6992   },
6993
6994   {
6995     Xballoon,                           TRUE,   FALSE,
6996     EL_BALLOON,                         -1, -1
6997   },
6998   {
6999     Yballoon_n,                         FALSE,  FALSE,
7000     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
7001   },
7002   {
7003     Yballoon_nB,                        FALSE,  TRUE,
7004     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
7005   },
7006   {
7007     Yballoon_e,                         FALSE,  FALSE,
7008     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
7009   },
7010   {
7011     Yballoon_eB,                        FALSE,  TRUE,
7012     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
7013   },
7014   {
7015     Yballoon_s,                         FALSE,  FALSE,
7016     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
7017   },
7018   {
7019     Yballoon_sB,                        FALSE,  TRUE,
7020     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
7021   },
7022   {
7023     Yballoon_w,                         FALSE,  FALSE,
7024     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
7025   },
7026   {
7027     Yballoon_wB,                        FALSE,  TRUE,
7028     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
7029   },
7030
7031   {
7032     Xball_1,                            TRUE,   FALSE,
7033     EL_EMC_MAGIC_BALL,                  -1, -1
7034   },
7035   {
7036     Yball_1,                            FALSE,  FALSE,
7037     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7038   },
7039   {
7040     Xball_2,                            FALSE,  FALSE,
7041     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7042   },
7043   {
7044     Yball_2,                            FALSE,  FALSE,
7045     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7046   },
7047   {
7048     Yball_blank,                        FALSE,  FALSE,
7049     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
7050   },
7051
7052   {
7053     Xamoeba_1,                          TRUE,   FALSE,
7054     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7055   },
7056   {
7057     Xamoeba_2,                          FALSE,  FALSE,
7058     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7059   },
7060   {
7061     Xamoeba_3,                          FALSE,  FALSE,
7062     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7063   },
7064   {
7065     Xamoeba_4,                          FALSE,  FALSE,
7066     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7067   },
7068   {
7069     Xamoeba_5,                          TRUE,   FALSE,
7070     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7071   },
7072   {
7073     Xamoeba_6,                          FALSE,  FALSE,
7074     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7075   },
7076   {
7077     Xamoeba_7,                          FALSE,  FALSE,
7078     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7079   },
7080   {
7081     Xamoeba_8,                          FALSE,  FALSE,
7082     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7083   },
7084
7085   {
7086     Xdrip,                              TRUE,   FALSE,
7087     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
7088   },
7089   {
7090     Xdrip_fall,                         FALSE,  FALSE,
7091     EL_AMOEBA_DROP,                     -1, -1
7092   },
7093   {
7094     Xdrip_stretch,                      FALSE,  FALSE,
7095     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7096   },
7097   {
7098     Xdrip_stretchB,                     FALSE,  TRUE,
7099     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7100   },
7101   {
7102     Ydrip_1_s,                          FALSE,  FALSE,
7103     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7104   },
7105   {
7106     Ydrip_1_sB,                         FALSE,  TRUE,
7107     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7108   },
7109   {
7110     Ydrip_2_s,                          FALSE,  FALSE,
7111     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7112   },
7113   {
7114     Ydrip_2_sB,                         FALSE,  TRUE,
7115     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7116   },
7117
7118   {
7119     Xwonderwall,                        TRUE,   FALSE,
7120     EL_MAGIC_WALL,                      -1, -1
7121   },
7122   {
7123     Ywonderwall,                        FALSE,  FALSE,
7124     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
7125   },
7126
7127   {
7128     Xwheel,                             TRUE,   FALSE,
7129     EL_ROBOT_WHEEL,                     -1, -1
7130   },
7131   {
7132     Ywheel,                             FALSE,  FALSE,
7133     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
7134   },
7135
7136   {
7137     Xswitch,                            TRUE,   FALSE,
7138     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
7139   },
7140   {
7141     Yswitch,                            FALSE,  FALSE,
7142     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
7143   },
7144
7145   {
7146     Xbumper,                            TRUE,   FALSE,
7147     EL_EMC_SPRING_BUMPER,               -1, -1
7148   },
7149   {
7150     Ybumper,                            FALSE,  FALSE,
7151     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
7152   },
7153
7154   {
7155     Xacid_nw,                           TRUE,   FALSE,
7156     EL_ACID_POOL_TOPLEFT,               -1, -1
7157   },
7158   {
7159     Xacid_ne,                           TRUE,   FALSE,
7160     EL_ACID_POOL_TOPRIGHT,              -1, -1
7161   },
7162   {
7163     Xacid_sw,                           TRUE,   FALSE,
7164     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
7165   },
7166   {
7167     Xacid_s,                            TRUE,   FALSE,
7168     EL_ACID_POOL_BOTTOM,                -1, -1
7169   },
7170   {
7171     Xacid_se,                           TRUE,   FALSE,
7172     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
7173   },
7174
7175   {
7176     Xfake_blank,                        TRUE,   FALSE,
7177     EL_INVISIBLE_WALL,                  -1, -1
7178   },
7179   {
7180     Yfake_blank,                        FALSE,  FALSE,
7181     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
7182   },
7183
7184   {
7185     Xfake_grass,                        TRUE,   FALSE,
7186     EL_EMC_FAKE_GRASS,                  -1, -1
7187   },
7188   {
7189     Yfake_grass,                        FALSE,  FALSE,
7190     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
7191   },
7192
7193   {
7194     Xfake_amoeba,                       TRUE,   FALSE,
7195     EL_EMC_DRIPPER,                     -1, -1
7196   },
7197   {
7198     Yfake_amoeba,                       FALSE,  FALSE,
7199     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
7200   },
7201
7202   {
7203     Xlenses,                            TRUE,   FALSE,
7204     EL_EMC_LENSES,                      -1, -1
7205   },
7206
7207   {
7208     Xmagnify,                           TRUE,   FALSE,
7209     EL_EMC_MAGNIFIER,                   -1, -1
7210   },
7211
7212   {
7213     Xsand,                              TRUE,   FALSE,
7214     EL_QUICKSAND_EMPTY,                 -1, -1
7215   },
7216   {
7217     Xsand_stone,                        TRUE,   FALSE,
7218     EL_QUICKSAND_FULL,                  -1, -1
7219   },
7220   {
7221     Xsand_stonein_1,                    FALSE,  TRUE,
7222     EL_ROCK,                            ACTION_FILLING, -1
7223   },
7224   {
7225     Xsand_stonein_2,                    FALSE,  TRUE,
7226     EL_ROCK,                            ACTION_FILLING, -1
7227   },
7228   {
7229     Xsand_stonein_3,                    FALSE,  TRUE,
7230     EL_ROCK,                            ACTION_FILLING, -1
7231   },
7232   {
7233     Xsand_stonein_4,                    FALSE,  TRUE,
7234     EL_ROCK,                            ACTION_FILLING, -1
7235   },
7236   {
7237     Xsand_sandstone_1,                  FALSE,  FALSE,
7238     EL_QUICKSAND_FILLING,               -1, -1
7239   },
7240   {
7241     Xsand_sandstone_2,                  FALSE,  FALSE,
7242     EL_QUICKSAND_FILLING,               -1, -1
7243   },
7244   {
7245     Xsand_sandstone_3,                  FALSE,  FALSE,
7246     EL_QUICKSAND_FILLING,               -1, -1
7247   },
7248   {
7249     Xsand_sandstone_4,                  FALSE,  FALSE,
7250     EL_QUICKSAND_FILLING,               -1, -1
7251   },
7252   {
7253     Xsand_stonesand_1,                  FALSE,  FALSE,
7254     EL_QUICKSAND_EMPTYING,              -1, -1
7255   },
7256   {
7257     Xsand_stonesand_2,                  FALSE,  FALSE,
7258     EL_QUICKSAND_EMPTYING,              -1, -1
7259   },
7260   {
7261     Xsand_stonesand_3,                  FALSE,  FALSE,
7262     EL_QUICKSAND_EMPTYING,              -1, -1
7263   },
7264   {
7265     Xsand_stonesand_4,                  FALSE,  FALSE,
7266     EL_QUICKSAND_EMPTYING,              -1, -1
7267   },
7268   {
7269     Xsand_stoneout_1,                   FALSE,  FALSE,
7270     EL_ROCK,                            ACTION_EMPTYING, -1
7271   },
7272   {
7273     Xsand_stoneout_2,                   FALSE,  FALSE,
7274     EL_ROCK,                            ACTION_EMPTYING, -1
7275   },
7276   {
7277     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
7278     EL_QUICKSAND_EMPTYING,              -1, -1
7279   },
7280   {
7281     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
7282     EL_QUICKSAND_EMPTYING,              -1, -1
7283   },
7284
7285   {
7286     Xslide_ns,                          TRUE,   FALSE,
7287     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
7288   },
7289   {
7290     Yslide_ns_blank,                    FALSE,  FALSE,
7291     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
7292   },
7293   {
7294     Xslide_ew,                          TRUE,   FALSE,
7295     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
7296   },
7297   {
7298     Yslide_ew_blank,                    FALSE,  FALSE,
7299     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
7300   },
7301
7302   {
7303     Xwind_n,                            TRUE,   FALSE,
7304     EL_BALLOON_SWITCH_UP,               -1, -1
7305   },
7306   {
7307     Xwind_e,                            TRUE,   FALSE,
7308     EL_BALLOON_SWITCH_RIGHT,            -1, -1
7309   },
7310   {
7311     Xwind_s,                            TRUE,   FALSE,
7312     EL_BALLOON_SWITCH_DOWN,             -1, -1
7313   },
7314   {
7315     Xwind_w,                            TRUE,   FALSE,
7316     EL_BALLOON_SWITCH_LEFT,             -1, -1
7317   },
7318   {
7319     Xwind_any,                          TRUE,   FALSE,
7320     EL_BALLOON_SWITCH_ANY,              -1, -1
7321   },
7322   {
7323     Xwind_stop,                         TRUE,   FALSE,
7324     EL_BALLOON_SWITCH_NONE,             -1, -1
7325   },
7326
7327   {
7328     Xexit,                              TRUE,   FALSE,
7329     EL_EM_EXIT_CLOSED,                  -1, -1
7330   },
7331   {
7332     Xexit_1,                            TRUE,   FALSE,
7333     EL_EM_EXIT_OPEN,                    -1, -1
7334   },
7335   {
7336     Xexit_2,                            FALSE,  FALSE,
7337     EL_EM_EXIT_OPEN,                    -1, -1
7338   },
7339   {
7340     Xexit_3,                            FALSE,  FALSE,
7341     EL_EM_EXIT_OPEN,                    -1, -1
7342   },
7343
7344   {
7345     Xpause,                             FALSE,  FALSE,
7346     EL_EMPTY,                           -1, -1
7347   },
7348
7349   {
7350     Xwall_1,                            TRUE,   FALSE,
7351     EL_WALL,                            -1, -1
7352   },
7353   {
7354     Xwall_2,                            TRUE,   FALSE,
7355     EL_EMC_WALL_14,                     -1, -1
7356   },
7357   {
7358     Xwall_3,                            TRUE,   FALSE,
7359     EL_EMC_WALL_15,                     -1, -1
7360   },
7361   {
7362     Xwall_4,                            TRUE,   FALSE,
7363     EL_EMC_WALL_16,                     -1, -1
7364   },
7365
7366   {
7367     Xroundwall_1,                       TRUE,   FALSE,
7368     EL_WALL_SLIPPERY,                   -1, -1
7369   },
7370   {
7371     Xroundwall_2,                       TRUE,   FALSE,
7372     EL_EMC_WALL_SLIPPERY_2,             -1, -1
7373   },
7374   {
7375     Xroundwall_3,                       TRUE,   FALSE,
7376     EL_EMC_WALL_SLIPPERY_3,             -1, -1
7377   },
7378   {
7379     Xroundwall_4,                       TRUE,   FALSE,
7380     EL_EMC_WALL_SLIPPERY_4,             -1, -1
7381   },
7382
7383   {
7384     Xsteel_1,                           TRUE,   FALSE,
7385     EL_STEELWALL,                       -1, -1
7386   },
7387   {
7388     Xsteel_2,                           TRUE,   FALSE,
7389     EL_EMC_STEELWALL_2,                 -1, -1
7390   },
7391   {
7392     Xsteel_3,                           TRUE,   FALSE,
7393     EL_EMC_STEELWALL_3,                 -1, -1
7394   },
7395   {
7396     Xsteel_4,                           TRUE,   FALSE,
7397     EL_EMC_STEELWALL_4,                 -1, -1
7398   },
7399
7400   {
7401     Xdecor_1,                           TRUE,   FALSE,
7402     EL_EMC_WALL_8,                      -1, -1
7403   },
7404   {
7405     Xdecor_2,                           TRUE,   FALSE,
7406     EL_EMC_WALL_6,                      -1, -1
7407   },
7408   {
7409     Xdecor_3,                           TRUE,   FALSE,
7410     EL_EMC_WALL_4,                      -1, -1
7411   },
7412   {
7413     Xdecor_4,                           TRUE,   FALSE,
7414     EL_EMC_WALL_7,                      -1, -1
7415   },
7416   {
7417     Xdecor_5,                           TRUE,   FALSE,
7418     EL_EMC_WALL_5,                      -1, -1
7419   },
7420   {
7421     Xdecor_6,                           TRUE,   FALSE,
7422     EL_EMC_WALL_9,                      -1, -1
7423   },
7424   {
7425     Xdecor_7,                           TRUE,   FALSE,
7426     EL_EMC_WALL_10,                     -1, -1
7427   },
7428   {
7429     Xdecor_8,                           TRUE,   FALSE,
7430     EL_EMC_WALL_1,                      -1, -1
7431   },
7432   {
7433     Xdecor_9,                           TRUE,   FALSE,
7434     EL_EMC_WALL_2,                      -1, -1
7435   },
7436   {
7437     Xdecor_10,                          TRUE,   FALSE,
7438     EL_EMC_WALL_3,                      -1, -1
7439   },
7440   {
7441     Xdecor_11,                          TRUE,   FALSE,
7442     EL_EMC_WALL_11,                     -1, -1
7443   },
7444   {
7445     Xdecor_12,                          TRUE,   FALSE,
7446     EL_EMC_WALL_12,                     -1, -1
7447   },
7448
7449   {
7450     Xalpha_0,                           TRUE,   FALSE,
7451     EL_CHAR('0'),                       -1, -1
7452   },
7453   {
7454     Xalpha_1,                           TRUE,   FALSE,
7455     EL_CHAR('1'),                       -1, -1
7456   },
7457   {
7458     Xalpha_2,                           TRUE,   FALSE,
7459     EL_CHAR('2'),                       -1, -1
7460   },
7461   {
7462     Xalpha_3,                           TRUE,   FALSE,
7463     EL_CHAR('3'),                       -1, -1
7464   },
7465   {
7466     Xalpha_4,                           TRUE,   FALSE,
7467     EL_CHAR('4'),                       -1, -1
7468   },
7469   {
7470     Xalpha_5,                           TRUE,   FALSE,
7471     EL_CHAR('5'),                       -1, -1
7472   },
7473   {
7474     Xalpha_6,                           TRUE,   FALSE,
7475     EL_CHAR('6'),                       -1, -1
7476   },
7477   {
7478     Xalpha_7,                           TRUE,   FALSE,
7479     EL_CHAR('7'),                       -1, -1
7480   },
7481   {
7482     Xalpha_8,                           TRUE,   FALSE,
7483     EL_CHAR('8'),                       -1, -1
7484   },
7485   {
7486     Xalpha_9,                           TRUE,   FALSE,
7487     EL_CHAR('9'),                       -1, -1
7488   },
7489   {
7490     Xalpha_excla,                       TRUE,   FALSE,
7491     EL_CHAR('!'),                       -1, -1
7492   },
7493   {
7494     Xalpha_apost,                       TRUE,   FALSE,
7495     EL_CHAR('\''),                      -1, -1
7496   },
7497   {
7498     Xalpha_comma,                       TRUE,   FALSE,
7499     EL_CHAR(','),                       -1, -1
7500   },
7501   {
7502     Xalpha_minus,                       TRUE,   FALSE,
7503     EL_CHAR('-'),                       -1, -1
7504   },
7505   {
7506     Xalpha_perio,                       TRUE,   FALSE,
7507     EL_CHAR('.'),                       -1, -1
7508   },
7509   {
7510     Xalpha_colon,                       TRUE,   FALSE,
7511     EL_CHAR(':'),                       -1, -1
7512   },
7513   {
7514     Xalpha_quest,                       TRUE,   FALSE,
7515     EL_CHAR('?'),                       -1, -1
7516   },
7517   {
7518     Xalpha_a,                           TRUE,   FALSE,
7519     EL_CHAR('A'),                       -1, -1
7520   },
7521   {
7522     Xalpha_b,                           TRUE,   FALSE,
7523     EL_CHAR('B'),                       -1, -1
7524   },
7525   {
7526     Xalpha_c,                           TRUE,   FALSE,
7527     EL_CHAR('C'),                       -1, -1
7528   },
7529   {
7530     Xalpha_d,                           TRUE,   FALSE,
7531     EL_CHAR('D'),                       -1, -1
7532   },
7533   {
7534     Xalpha_e,                           TRUE,   FALSE,
7535     EL_CHAR('E'),                       -1, -1
7536   },
7537   {
7538     Xalpha_f,                           TRUE,   FALSE,
7539     EL_CHAR('F'),                       -1, -1
7540   },
7541   {
7542     Xalpha_g,                           TRUE,   FALSE,
7543     EL_CHAR('G'),                       -1, -1
7544   },
7545   {
7546     Xalpha_h,                           TRUE,   FALSE,
7547     EL_CHAR('H'),                       -1, -1
7548   },
7549   {
7550     Xalpha_i,                           TRUE,   FALSE,
7551     EL_CHAR('I'),                       -1, -1
7552   },
7553   {
7554     Xalpha_j,                           TRUE,   FALSE,
7555     EL_CHAR('J'),                       -1, -1
7556   },
7557   {
7558     Xalpha_k,                           TRUE,   FALSE,
7559     EL_CHAR('K'),                       -1, -1
7560   },
7561   {
7562     Xalpha_l,                           TRUE,   FALSE,
7563     EL_CHAR('L'),                       -1, -1
7564   },
7565   {
7566     Xalpha_m,                           TRUE,   FALSE,
7567     EL_CHAR('M'),                       -1, -1
7568   },
7569   {
7570     Xalpha_n,                           TRUE,   FALSE,
7571     EL_CHAR('N'),                       -1, -1
7572   },
7573   {
7574     Xalpha_o,                           TRUE,   FALSE,
7575     EL_CHAR('O'),                       -1, -1
7576   },
7577   {
7578     Xalpha_p,                           TRUE,   FALSE,
7579     EL_CHAR('P'),                       -1, -1
7580   },
7581   {
7582     Xalpha_q,                           TRUE,   FALSE,
7583     EL_CHAR('Q'),                       -1, -1
7584   },
7585   {
7586     Xalpha_r,                           TRUE,   FALSE,
7587     EL_CHAR('R'),                       -1, -1
7588   },
7589   {
7590     Xalpha_s,                           TRUE,   FALSE,
7591     EL_CHAR('S'),                       -1, -1
7592   },
7593   {
7594     Xalpha_t,                           TRUE,   FALSE,
7595     EL_CHAR('T'),                       -1, -1
7596   },
7597   {
7598     Xalpha_u,                           TRUE,   FALSE,
7599     EL_CHAR('U'),                       -1, -1
7600   },
7601   {
7602     Xalpha_v,                           TRUE,   FALSE,
7603     EL_CHAR('V'),                       -1, -1
7604   },
7605   {
7606     Xalpha_w,                           TRUE,   FALSE,
7607     EL_CHAR('W'),                       -1, -1
7608   },
7609   {
7610     Xalpha_x,                           TRUE,   FALSE,
7611     EL_CHAR('X'),                       -1, -1
7612   },
7613   {
7614     Xalpha_y,                           TRUE,   FALSE,
7615     EL_CHAR('Y'),                       -1, -1
7616   },
7617   {
7618     Xalpha_z,                           TRUE,   FALSE,
7619     EL_CHAR('Z'),                       -1, -1
7620   },
7621   {
7622     Xalpha_arrow_e,                     TRUE,   FALSE,
7623     EL_CHAR('>'),                       -1, -1
7624   },
7625   {
7626     Xalpha_arrow_w,                     TRUE,   FALSE,
7627     EL_CHAR('<'),                       -1, -1
7628   },
7629   {
7630     Xalpha_copyr,                       TRUE,   FALSE,
7631     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
7632   },
7633
7634   {
7635     Ykey_1_blank,                       FALSE,  FALSE,
7636     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
7637   },
7638   {
7639     Ykey_2_blank,                       FALSE,  FALSE,
7640     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
7641   },
7642   {
7643     Ykey_3_blank,                       FALSE,  FALSE,
7644     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
7645   },
7646   {
7647     Ykey_4_blank,                       FALSE,  FALSE,
7648     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
7649   },
7650   {
7651     Ykey_5_blank,                       FALSE,  FALSE,
7652     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
7653   },
7654   {
7655     Ykey_6_blank,                       FALSE,  FALSE,
7656     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
7657   },
7658   {
7659     Ykey_7_blank,                       FALSE,  FALSE,
7660     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
7661   },
7662   {
7663     Ykey_8_blank,                       FALSE,  FALSE,
7664     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
7665   },
7666   {
7667     Ylenses_blank,                      FALSE,  FALSE,
7668     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
7669   },
7670   {
7671     Ymagnify_blank,                     FALSE,  FALSE,
7672     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
7673   },
7674   {
7675     Ygrass_blank,                       FALSE,  FALSE,
7676     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
7677   },
7678   {
7679     Ydirt_blank,                        FALSE,  FALSE,
7680     EL_SAND,                            ACTION_SNAPPING, -1
7681   },
7682
7683   {
7684     -1,                                 FALSE,  FALSE,
7685     -1,                                 -1, -1
7686   }
7687 };
7688
7689 static struct Mapping_EM_to_RND_player
7690 {
7691   int action_em;
7692   int player_nr;
7693
7694   int element_rnd;
7695   int action;
7696   int direction;
7697 }
7698 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7699 {
7700   {
7701     PLY_walk_n,                         0,
7702     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
7703   },
7704   {
7705     PLY_walk_e,                         0,
7706     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
7707   },
7708   {
7709     PLY_walk_s,                         0,
7710     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
7711   },
7712   {
7713     PLY_walk_w,                         0,
7714     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
7715   },
7716   {
7717     PLY_push_n,                         0,
7718     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
7719   },
7720   {
7721     PLY_push_e,                         0,
7722     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
7723   },
7724   {
7725     PLY_push_s,                         0,
7726     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
7727   },
7728   {
7729     PLY_push_w,                         0,
7730     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
7731   },
7732   {
7733     PLY_shoot_n,                        0,
7734     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
7735   },
7736   {
7737     PLY_shoot_e,                        0,
7738     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7739   },
7740   {
7741     PLY_shoot_s,                        0,
7742     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
7743   },
7744   {
7745     PLY_shoot_w,                        0,
7746     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
7747   },
7748   {
7749     PLY_walk_n,                         1,
7750     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
7751   },
7752   {
7753     PLY_walk_e,                         1,
7754     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
7755   },
7756   {
7757     PLY_walk_s,                         1,
7758     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
7759   },
7760   {
7761     PLY_walk_w,                         1,
7762     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
7763   },
7764   {
7765     PLY_push_n,                         1,
7766     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
7767   },
7768   {
7769     PLY_push_e,                         1,
7770     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
7771   },
7772   {
7773     PLY_push_s,                         1,
7774     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
7775   },
7776   {
7777     PLY_push_w,                         1,
7778     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
7779   },
7780   {
7781     PLY_shoot_n,                        1,
7782     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
7783   },
7784   {
7785     PLY_shoot_e,                        1,
7786     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7787   },
7788   {
7789     PLY_shoot_s,                        1,
7790     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
7791   },
7792   {
7793     PLY_shoot_w,                        1,
7794     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
7795   },
7796   {
7797     PLY_still,                          0,
7798     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
7799   },
7800   {
7801     PLY_still,                          1,
7802     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
7803   },
7804   {
7805     PLY_walk_n,                         2,
7806     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
7807   },
7808   {
7809     PLY_walk_e,                         2,
7810     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
7811   },
7812   {
7813     PLY_walk_s,                         2,
7814     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
7815   },
7816   {
7817     PLY_walk_w,                         2,
7818     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
7819   },
7820   {
7821     PLY_push_n,                         2,
7822     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
7823   },
7824   {
7825     PLY_push_e,                         2,
7826     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
7827   },
7828   {
7829     PLY_push_s,                         2,
7830     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
7831   },
7832   {
7833     PLY_push_w,                         2,
7834     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
7835   },
7836   {
7837     PLY_shoot_n,                        2,
7838     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
7839   },
7840   {
7841     PLY_shoot_e,                        2,
7842     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7843   },
7844   {
7845     PLY_shoot_s,                        2,
7846     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
7847   },
7848   {
7849     PLY_shoot_w,                        2,
7850     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
7851   },
7852   {
7853     PLY_walk_n,                         3,
7854     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
7855   },
7856   {
7857     PLY_walk_e,                         3,
7858     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
7859   },
7860   {
7861     PLY_walk_s,                         3,
7862     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
7863   },
7864   {
7865     PLY_walk_w,                         3,
7866     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
7867   },
7868   {
7869     PLY_push_n,                         3,
7870     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
7871   },
7872   {
7873     PLY_push_e,                         3,
7874     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
7875   },
7876   {
7877     PLY_push_s,                         3,
7878     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
7879   },
7880   {
7881     PLY_push_w,                         3,
7882     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
7883   },
7884   {
7885     PLY_shoot_n,                        3,
7886     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
7887   },
7888   {
7889     PLY_shoot_e,                        3,
7890     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7891   },
7892   {
7893     PLY_shoot_s,                        3,
7894     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
7895   },
7896   {
7897     PLY_shoot_w,                        3,
7898     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
7899   },
7900   {
7901     PLY_still,                          2,
7902     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
7903   },
7904   {
7905     PLY_still,                          3,
7906     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7907   },
7908
7909   {
7910     -1,                                 -1,
7911     -1,                                 -1, -1
7912   }
7913 };
7914
7915 int map_element_RND_to_EM_cave(int element_rnd)
7916 {
7917   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7918   static boolean mapping_initialized = FALSE;
7919
7920   if (!mapping_initialized)
7921   {
7922     int i;
7923
7924     // return "Xalpha_quest" for all undefined elements in mapping array
7925     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7926       mapping_RND_to_EM[i] = Xalpha_quest;
7927
7928     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7929       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7930         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7931           em_object_mapping_list[i].element_em;
7932
7933     mapping_initialized = TRUE;
7934   }
7935
7936   if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7937   {
7938     Warn("invalid RND level element %d", element_rnd);
7939
7940     return EL_UNKNOWN;
7941   }
7942
7943   return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7944 }
7945
7946 int map_element_EM_to_RND_cave(int element_em_cave)
7947 {
7948   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7949   static boolean mapping_initialized = FALSE;
7950
7951   if (!mapping_initialized)
7952   {
7953     int i;
7954
7955     // return "EL_UNKNOWN" for all undefined elements in mapping array
7956     for (i = 0; i < GAME_TILE_MAX; i++)
7957       mapping_EM_to_RND[i] = EL_UNKNOWN;
7958
7959     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7960       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7961         em_object_mapping_list[i].element_rnd;
7962
7963     mapping_initialized = TRUE;
7964   }
7965
7966   if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7967   {
7968     Warn("invalid EM cave element %d", element_em_cave);
7969
7970     return EL_UNKNOWN;
7971   }
7972
7973   return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7974 }
7975
7976 int map_element_EM_to_RND_game(int element_em_game)
7977 {
7978   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7979   static boolean mapping_initialized = FALSE;
7980
7981   if (!mapping_initialized)
7982   {
7983     int i;
7984
7985     // return "EL_UNKNOWN" for all undefined elements in mapping array
7986     for (i = 0; i < GAME_TILE_MAX; i++)
7987       mapping_EM_to_RND[i] = EL_UNKNOWN;
7988
7989     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7990       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7991         em_object_mapping_list[i].element_rnd;
7992
7993     mapping_initialized = TRUE;
7994   }
7995
7996   if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7997   {
7998     Warn("invalid EM game element %d", element_em_game);
7999
8000     return EL_UNKNOWN;
8001   }
8002
8003   return mapping_EM_to_RND[element_em_game];
8004 }
8005
8006 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8007 {
8008   struct LevelInfo_EM *level_em = level->native_em_level;
8009   struct CAVE *cav = level_em->cav;
8010   int i, j;
8011
8012   for (i = 0; i < GAME_TILE_MAX; i++)
8013     cav->android_array[i] = Cblank;
8014
8015   for (i = 0; i < level->num_android_clone_elements; i++)
8016   {
8017     int element_rnd = level->android_clone_element[i];
8018     int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8019
8020     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8021       if (em_object_mapping_list[j].element_rnd == element_rnd)
8022         cav->android_array[em_object_mapping_list[j].element_em] =
8023           element_em_cave;
8024   }
8025 }
8026
8027 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8028 {
8029   struct LevelInfo_EM *level_em = level->native_em_level;
8030   struct CAVE *cav = level_em->cav;
8031   int i, j;
8032
8033   level->num_android_clone_elements = 0;
8034
8035   for (i = 0; i < GAME_TILE_MAX; i++)
8036   {
8037     int element_em_cave = cav->android_array[i];
8038     int element_rnd;
8039     boolean element_found = FALSE;
8040
8041     if (element_em_cave == Cblank)
8042       continue;
8043
8044     element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8045
8046     for (j = 0; j < level->num_android_clone_elements; j++)
8047       if (level->android_clone_element[j] == element_rnd)
8048         element_found = TRUE;
8049
8050     if (!element_found)
8051     {
8052       level->android_clone_element[level->num_android_clone_elements++] =
8053         element_rnd;
8054
8055       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8056         break;
8057     }
8058   }
8059
8060   if (level->num_android_clone_elements == 0)
8061   {
8062     level->num_android_clone_elements = 1;
8063     level->android_clone_element[0] = EL_EMPTY;
8064   }
8065 }
8066
8067 int map_direction_RND_to_EM(int direction)
8068 {
8069   return (direction == MV_UP    ? 0 :
8070           direction == MV_RIGHT ? 1 :
8071           direction == MV_DOWN  ? 2 :
8072           direction == MV_LEFT  ? 3 :
8073           -1);
8074 }
8075
8076 int map_direction_EM_to_RND(int direction)
8077 {
8078   return (direction == 0 ? MV_UP    :
8079           direction == 1 ? MV_RIGHT :
8080           direction == 2 ? MV_DOWN  :
8081           direction == 3 ? MV_LEFT  :
8082           MV_NONE);
8083 }
8084
8085 int map_element_RND_to_SP(int element_rnd)
8086 {
8087   int element_sp = 0x20;        // map unknown elements to yellow "hardware"
8088
8089   if (element_rnd >= EL_SP_START &&
8090       element_rnd <= EL_SP_END)
8091     element_sp = element_rnd - EL_SP_START;
8092   else if (element_rnd == EL_EMPTY_SPACE)
8093     element_sp = 0x00;
8094   else if (element_rnd == EL_INVISIBLE_WALL)
8095     element_sp = 0x28;
8096
8097   return element_sp;
8098 }
8099
8100 int map_element_SP_to_RND(int element_sp)
8101 {
8102   int element_rnd = EL_UNKNOWN;
8103
8104   if (element_sp >= 0x00 &&
8105       element_sp <= 0x27)
8106     element_rnd = EL_SP_START + element_sp;
8107   else if (element_sp == 0x28)
8108     element_rnd = EL_INVISIBLE_WALL;
8109
8110   return element_rnd;
8111 }
8112
8113 int map_action_SP_to_RND(int action_sp)
8114 {
8115   switch (action_sp)
8116   {
8117     case actActive:             return ACTION_ACTIVE;
8118     case actImpact:             return ACTION_IMPACT;
8119     case actExploding:          return ACTION_EXPLODING;
8120     case actDigging:            return ACTION_DIGGING;
8121     case actSnapping:           return ACTION_SNAPPING;
8122     case actCollecting:         return ACTION_COLLECTING;
8123     case actPassing:            return ACTION_PASSING;
8124     case actPushing:            return ACTION_PUSHING;
8125     case actDropping:           return ACTION_DROPPING;
8126
8127     default:                    return ACTION_DEFAULT;
8128   }
8129 }
8130
8131 int map_element_RND_to_MM(int element_rnd)
8132 {
8133   return (element_rnd >= EL_MM_START_1 &&
8134           element_rnd <= EL_MM_END_1 ?
8135           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8136
8137           element_rnd >= EL_MM_START_2 &&
8138           element_rnd <= EL_MM_END_2 ?
8139           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8140
8141           element_rnd >= EL_CHAR_START &&
8142           element_rnd <= EL_CHAR_END ?
8143           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8144
8145           element_rnd >= EL_MM_RUNTIME_START &&
8146           element_rnd <= EL_MM_RUNTIME_END ?
8147           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8148
8149           element_rnd >= EL_MM_DUMMY_START &&
8150           element_rnd <= EL_MM_DUMMY_END ?
8151           EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8152
8153           EL_MM_EMPTY_NATIVE);
8154 }
8155
8156 int map_element_MM_to_RND(int element_mm)
8157 {
8158   return (element_mm == EL_MM_EMPTY_NATIVE ||
8159           element_mm == EL_DF_EMPTY_NATIVE ?
8160           EL_EMPTY :
8161
8162           element_mm >= EL_MM_START_1_NATIVE &&
8163           element_mm <= EL_MM_END_1_NATIVE ?
8164           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8165
8166           element_mm >= EL_MM_START_2_NATIVE &&
8167           element_mm <= EL_MM_END_2_NATIVE ?
8168           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8169
8170           element_mm >= EL_MM_CHAR_START_NATIVE &&
8171           element_mm <= EL_MM_CHAR_END_NATIVE ?
8172           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8173
8174           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8175           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8176           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8177
8178           element_mm >= EL_MM_DUMMY_START_NATIVE &&
8179           element_mm <= EL_MM_DUMMY_END_NATIVE ?
8180           EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8181
8182           EL_EMPTY);
8183 }
8184
8185 int map_action_MM_to_RND(int action_mm)
8186 {
8187   // all MM actions are defined to exactly match their RND counterparts
8188   return action_mm;
8189 }
8190
8191 int map_sound_MM_to_RND(int sound_mm)
8192 {
8193   switch (sound_mm)
8194   {
8195     case SND_MM_GAME_LEVELTIME_CHARGING:
8196       return SND_GAME_LEVELTIME_CHARGING;
8197
8198     case SND_MM_GAME_HEALTH_CHARGING:
8199       return SND_GAME_HEALTH_CHARGING;
8200
8201     default:
8202       return SND_UNDEFINED;
8203   }
8204 }
8205
8206 int map_mm_wall_element(int element)
8207 {
8208   return (element >= EL_MM_STEEL_WALL_START &&
8209           element <= EL_MM_STEEL_WALL_END ?
8210           EL_MM_STEEL_WALL :
8211
8212           element >= EL_MM_WOODEN_WALL_START &&
8213           element <= EL_MM_WOODEN_WALL_END ?
8214           EL_MM_WOODEN_WALL :
8215
8216           element >= EL_MM_ICE_WALL_START &&
8217           element <= EL_MM_ICE_WALL_END ?
8218           EL_MM_ICE_WALL :
8219
8220           element >= EL_MM_AMOEBA_WALL_START &&
8221           element <= EL_MM_AMOEBA_WALL_END ?
8222           EL_MM_AMOEBA_WALL :
8223
8224           element >= EL_DF_STEEL_WALL_START &&
8225           element <= EL_DF_STEEL_WALL_END ?
8226           EL_DF_STEEL_WALL :
8227
8228           element >= EL_DF_WOODEN_WALL_START &&
8229           element <= EL_DF_WOODEN_WALL_END ?
8230           EL_DF_WOODEN_WALL :
8231
8232           element);
8233 }
8234
8235 int map_mm_wall_element_editor(int element)
8236 {
8237   switch (element)
8238   {
8239     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
8240     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
8241     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
8242     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
8243     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
8244     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
8245
8246     default:                    return element;
8247   }
8248 }
8249
8250 int get_next_element(int element)
8251 {
8252   switch (element)
8253   {
8254     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
8255     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
8256     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
8257     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
8258     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
8259     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
8260     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
8261     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
8262     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
8263     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
8264     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
8265
8266     default:                            return element;
8267   }
8268 }
8269
8270 int el2img_mm(int element_mm)
8271 {
8272   return el2img(map_element_MM_to_RND(element_mm));
8273 }
8274
8275 int el_act_dir2img(int element, int action, int direction)
8276 {
8277   element = GFX_ELEMENT(element);
8278   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8279
8280   // direction_graphic[][] == graphic[] for undefined direction graphics
8281   return element_info[element].direction_graphic[action][direction];
8282 }
8283
8284 static int el_act_dir2crm(int element, int action, int direction)
8285 {
8286   element = GFX_ELEMENT(element);
8287   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8288
8289   // direction_graphic[][] == graphic[] for undefined direction graphics
8290   return element_info[element].direction_crumbled[action][direction];
8291 }
8292
8293 int el_act2img(int element, int action)
8294 {
8295   element = GFX_ELEMENT(element);
8296
8297   return element_info[element].graphic[action];
8298 }
8299
8300 int el_act2crm(int element, int action)
8301 {
8302   element = GFX_ELEMENT(element);
8303
8304   return element_info[element].crumbled[action];
8305 }
8306
8307 int el_dir2img(int element, int direction)
8308 {
8309   element = GFX_ELEMENT(element);
8310
8311   return el_act_dir2img(element, ACTION_DEFAULT, direction);
8312 }
8313
8314 int el2baseimg(int element)
8315 {
8316   return element_info[element].graphic[ACTION_DEFAULT];
8317 }
8318
8319 int el2img(int element)
8320 {
8321   element = GFX_ELEMENT(element);
8322
8323   return element_info[element].graphic[ACTION_DEFAULT];
8324 }
8325
8326 int el2edimg(int element)
8327 {
8328   element = GFX_ELEMENT(element);
8329
8330   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8331 }
8332
8333 int el2preimg(int element)
8334 {
8335   element = GFX_ELEMENT(element);
8336
8337   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8338 }
8339
8340 int el2panelimg(int element)
8341 {
8342   element = GFX_ELEMENT(element);
8343
8344   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8345 }
8346
8347 int font2baseimg(int font_nr)
8348 {
8349   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8350 }
8351
8352 int getBeltNrFromBeltElement(int element)
8353 {
8354   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8355           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8356           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8357 }
8358
8359 int getBeltNrFromBeltActiveElement(int element)
8360 {
8361   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8362           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8363           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8364 }
8365
8366 int getBeltNrFromBeltSwitchElement(int element)
8367 {
8368   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8369           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8370           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8371 }
8372
8373 int getBeltDirNrFromBeltElement(int element)
8374 {
8375   static int belt_base_element[4] =
8376   {
8377     EL_CONVEYOR_BELT_1_LEFT,
8378     EL_CONVEYOR_BELT_2_LEFT,
8379     EL_CONVEYOR_BELT_3_LEFT,
8380     EL_CONVEYOR_BELT_4_LEFT
8381   };
8382
8383   int belt_nr = getBeltNrFromBeltElement(element);
8384   int belt_dir_nr = element - belt_base_element[belt_nr];
8385
8386   return (belt_dir_nr % 3);
8387 }
8388
8389 int getBeltDirNrFromBeltSwitchElement(int element)
8390 {
8391   static int belt_base_element[4] =
8392   {
8393     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8394     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8395     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8396     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8397   };
8398
8399   int belt_nr = getBeltNrFromBeltSwitchElement(element);
8400   int belt_dir_nr = element - belt_base_element[belt_nr];
8401
8402   return (belt_dir_nr % 3);
8403 }
8404
8405 int getBeltDirFromBeltElement(int element)
8406 {
8407   static int belt_move_dir[3] =
8408   {
8409     MV_LEFT,
8410     MV_NONE,
8411     MV_RIGHT
8412   };
8413
8414   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8415
8416   return belt_move_dir[belt_dir_nr];
8417 }
8418
8419 int getBeltDirFromBeltSwitchElement(int element)
8420 {
8421   static int belt_move_dir[3] =
8422   {
8423     MV_LEFT,
8424     MV_NONE,
8425     MV_RIGHT
8426   };
8427
8428   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8429
8430   return belt_move_dir[belt_dir_nr];
8431 }
8432
8433 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8434 {
8435   static int belt_base_element[4] =
8436   {
8437     EL_CONVEYOR_BELT_1_LEFT,
8438     EL_CONVEYOR_BELT_2_LEFT,
8439     EL_CONVEYOR_BELT_3_LEFT,
8440     EL_CONVEYOR_BELT_4_LEFT
8441   };
8442
8443   return belt_base_element[belt_nr] + belt_dir_nr;
8444 }
8445
8446 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8447 {
8448   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8449
8450   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8451 }
8452
8453 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8454 {
8455   static int belt_base_element[4] =
8456   {
8457     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8458     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8459     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8460     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8461   };
8462
8463   return belt_base_element[belt_nr] + belt_dir_nr;
8464 }
8465
8466 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8467 {
8468   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8469
8470   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8471 }
8472
8473 boolean swapTiles_EM(boolean is_pre_emc_cave)
8474 {
8475   return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8476 }
8477
8478 boolean getTeamMode_EM(void)
8479 {
8480   return game.team_mode || network_playing;
8481 }
8482
8483 boolean isActivePlayer_EM(int player_nr)
8484 {
8485   return stored_player[player_nr].active;
8486 }
8487
8488 unsigned int InitRND(int seed)
8489 {
8490   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8491     return InitEngineRandom_EM(seed);
8492   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8493     return InitEngineRandom_SP(seed);
8494   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8495     return InitEngineRandom_MM(seed);
8496   else
8497     return InitEngineRandom_RND(seed);
8498 }
8499
8500 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8501 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8502
8503 static int get_effective_element_EM(int tile, int frame_em)
8504 {
8505   int element             = object_mapping[tile].element_rnd;
8506   int action              = object_mapping[tile].action;
8507   boolean is_backside     = object_mapping[tile].is_backside;
8508   boolean action_removing = (action == ACTION_DIGGING ||
8509                              action == ACTION_SNAPPING ||
8510                              action == ACTION_COLLECTING);
8511
8512   if (frame_em < 7)
8513   {
8514     switch (tile)
8515     {
8516       case Xsplash_e:
8517       case Xsplash_w:
8518         return (frame_em > 5 ? EL_EMPTY : element);
8519
8520       default:
8521         return element;
8522     }
8523   }
8524   else  // frame_em == 7
8525   {
8526     switch (tile)
8527     {
8528       case Xsplash_e:
8529       case Xsplash_w:
8530         return EL_EMPTY;
8531
8532       case Ynut_stone:
8533         return EL_EMERALD;
8534
8535       case Ydiamond_stone:
8536         return EL_ROCK;
8537
8538       case Xdrip_stretch:
8539       case Xdrip_stretchB:
8540       case Ydrip_1_s:
8541       case Ydrip_1_sB:
8542       case Yball_1:
8543       case Xball_2:
8544       case Yball_2:
8545       case Yball_blank:
8546       case Ykey_1_blank:
8547       case Ykey_2_blank:
8548       case Ykey_3_blank:
8549       case Ykey_4_blank:
8550       case Ykey_5_blank:
8551       case Ykey_6_blank:
8552       case Ykey_7_blank:
8553       case Ykey_8_blank:
8554       case Ylenses_blank:
8555       case Ymagnify_blank:
8556       case Ygrass_blank:
8557       case Ydirt_blank:
8558       case Xsand_stonein_1:
8559       case Xsand_stonein_2:
8560       case Xsand_stonein_3:
8561       case Xsand_stonein_4:
8562         return element;
8563
8564       default:
8565         return (is_backside || action_removing ? EL_EMPTY : element);
8566     }
8567   }
8568 }
8569
8570 static boolean check_linear_animation_EM(int tile)
8571 {
8572   switch (tile)
8573   {
8574     case Xsand_stonesand_1:
8575     case Xsand_stonesand_quickout_1:
8576     case Xsand_sandstone_1:
8577     case Xsand_stonein_1:
8578     case Xsand_stoneout_1:
8579     case Xboom_1:
8580     case Xdynamite_1:
8581     case Ybug_w_n:
8582     case Ybug_n_e:
8583     case Ybug_e_s:
8584     case Ybug_s_w:
8585     case Ybug_e_n:
8586     case Ybug_s_e:
8587     case Ybug_w_s:
8588     case Ybug_n_w:
8589     case Ytank_w_n:
8590     case Ytank_n_e:
8591     case Ytank_e_s:
8592     case Ytank_s_w:
8593     case Ytank_e_n:
8594     case Ytank_s_e:
8595     case Ytank_w_s:
8596     case Ytank_n_w:
8597     case Xsplash_e:
8598     case Xsplash_w:
8599     case Ynut_stone:
8600       return TRUE;
8601   }
8602
8603   return FALSE;
8604 }
8605
8606 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8607                                      boolean has_crumbled_graphics,
8608                                      int crumbled, int sync_frame)
8609 {
8610   // if element can be crumbled, but certain action graphics are just empty
8611   // space (like instantly snapping sand to empty space in 1 frame), do not
8612   // treat these empty space graphics as crumbled graphics in EMC engine
8613   if (crumbled == IMG_EMPTY_SPACE)
8614     has_crumbled_graphics = FALSE;
8615
8616   if (has_crumbled_graphics)
8617   {
8618     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8619     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8620                                            g_crumbled->anim_delay,
8621                                            g_crumbled->anim_mode,
8622                                            g_crumbled->anim_start_frame,
8623                                            sync_frame);
8624
8625     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8626                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8627
8628     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8629     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8630
8631     g_em->has_crumbled_graphics = TRUE;
8632   }
8633   else
8634   {
8635     g_em->crumbled_bitmap = NULL;
8636     g_em->crumbled_src_x = 0;
8637     g_em->crumbled_src_y = 0;
8638     g_em->crumbled_border_size = 0;
8639     g_em->crumbled_tile_size = 0;
8640
8641     g_em->has_crumbled_graphics = FALSE;
8642   }
8643 }
8644
8645 #if 0
8646 void ResetGfxAnimation_EM(int x, int y, int tile)
8647 {
8648   GfxFrame[x][y] = 0;
8649 }
8650 #endif
8651
8652 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8653                         int tile, int frame_em, int x, int y)
8654 {
8655   int action = object_mapping[tile].action;
8656   int direction = object_mapping[tile].direction;
8657   int effective_element = get_effective_element_EM(tile, frame_em);
8658   int graphic = (direction == MV_NONE ?
8659                  el_act2img(effective_element, action) :
8660                  el_act_dir2img(effective_element, action, direction));
8661   struct GraphicInfo *g = &graphic_info[graphic];
8662   int sync_frame;
8663   boolean action_removing = (action == ACTION_DIGGING ||
8664                              action == ACTION_SNAPPING ||
8665                              action == ACTION_COLLECTING);
8666   boolean action_moving   = (action == ACTION_FALLING ||
8667                              action == ACTION_MOVING ||
8668                              action == ACTION_PUSHING ||
8669                              action == ACTION_EATING ||
8670                              action == ACTION_FILLING ||
8671                              action == ACTION_EMPTYING);
8672   boolean action_falling  = (action == ACTION_FALLING ||
8673                              action == ACTION_FILLING ||
8674                              action == ACTION_EMPTYING);
8675
8676   // special case: graphic uses "2nd movement tile" and has defined
8677   // 7 frames for movement animation (or less) => use default graphic
8678   // for last (8th) frame which ends the movement animation
8679   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8680   {
8681     action = ACTION_DEFAULT;    // (keep action_* unchanged for now)
8682     graphic = (direction == MV_NONE ?
8683                el_act2img(effective_element, action) :
8684                el_act_dir2img(effective_element, action, direction));
8685
8686     g = &graphic_info[graphic];
8687   }
8688
8689   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8690   {
8691     GfxFrame[x][y] = 0;
8692   }
8693   else if (action_moving)
8694   {
8695     boolean is_backside = object_mapping[tile].is_backside;
8696
8697     if (is_backside)
8698     {
8699       int direction = object_mapping[tile].direction;
8700       int move_dir = (action_falling ? MV_DOWN : direction);
8701
8702       GfxFrame[x][y]++;
8703
8704 #if 1
8705       // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8706       if (g->double_movement && frame_em == 0)
8707         GfxFrame[x][y] = 0;
8708 #endif
8709
8710       if (move_dir == MV_LEFT)
8711         GfxFrame[x - 1][y] = GfxFrame[x][y];
8712       else if (move_dir == MV_RIGHT)
8713         GfxFrame[x + 1][y] = GfxFrame[x][y];
8714       else if (move_dir == MV_UP)
8715         GfxFrame[x][y - 1] = GfxFrame[x][y];
8716       else if (move_dir == MV_DOWN)
8717         GfxFrame[x][y + 1] = GfxFrame[x][y];
8718     }
8719   }
8720   else
8721   {
8722     GfxFrame[x][y]++;
8723
8724     // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8725     if (tile == Xsand_stonesand_quickout_1 ||
8726         tile == Xsand_stonesand_quickout_2)
8727       GfxFrame[x][y]++;
8728   }
8729
8730   if (graphic_info[graphic].anim_global_sync)
8731     sync_frame = FrameCounter;
8732   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8733     sync_frame = GfxFrame[x][y];
8734   else
8735     sync_frame = 0;     // playfield border (pseudo steel)
8736
8737   SetRandomAnimationValue(x, y);
8738
8739   int frame = getAnimationFrame(g->anim_frames,
8740                                 g->anim_delay,
8741                                 g->anim_mode,
8742                                 g->anim_start_frame,
8743                                 sync_frame);
8744
8745   g_em->unique_identifier =
8746     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8747 }
8748
8749 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8750                                   int tile, int frame_em, int x, int y)
8751 {
8752   int action = object_mapping[tile].action;
8753   int direction = object_mapping[tile].direction;
8754   boolean is_backside = object_mapping[tile].is_backside;
8755   int effective_element = get_effective_element_EM(tile, frame_em);
8756   int effective_action = action;
8757   int graphic = (direction == MV_NONE ?
8758                  el_act2img(effective_element, effective_action) :
8759                  el_act_dir2img(effective_element, effective_action,
8760                                 direction));
8761   int crumbled = (direction == MV_NONE ?
8762                   el_act2crm(effective_element, effective_action) :
8763                   el_act_dir2crm(effective_element, effective_action,
8764                                  direction));
8765   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8766   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8767   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8768   struct GraphicInfo *g = &graphic_info[graphic];
8769   int sync_frame;
8770
8771   // special case: graphic uses "2nd movement tile" and has defined
8772   // 7 frames for movement animation (or less) => use default graphic
8773   // for last (8th) frame which ends the movement animation
8774   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8775   {
8776     effective_action = ACTION_DEFAULT;
8777     graphic = (direction == MV_NONE ?
8778                el_act2img(effective_element, effective_action) :
8779                el_act_dir2img(effective_element, effective_action,
8780                               direction));
8781     crumbled = (direction == MV_NONE ?
8782                 el_act2crm(effective_element, effective_action) :
8783                 el_act_dir2crm(effective_element, effective_action,
8784                                direction));
8785
8786     g = &graphic_info[graphic];
8787   }
8788
8789   if (graphic_info[graphic].anim_global_sync)
8790     sync_frame = FrameCounter;
8791   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8792     sync_frame = GfxFrame[x][y];
8793   else
8794     sync_frame = 0;     // playfield border (pseudo steel)
8795
8796   SetRandomAnimationValue(x, y);
8797
8798   int frame = getAnimationFrame(g->anim_frames,
8799                                 g->anim_delay,
8800                                 g->anim_mode,
8801                                 g->anim_start_frame,
8802                                 sync_frame);
8803
8804   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8805                       g->double_movement && is_backside);
8806
8807   // (updating the "crumbled" graphic definitions is probably not really needed,
8808   // as animations for crumbled graphics can't be longer than one EMC cycle)
8809   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8810                            sync_frame);
8811 }
8812
8813 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8814                                   int player_nr, int anim, int frame_em)
8815 {
8816   int element   = player_mapping[player_nr][anim].element_rnd;
8817   int action    = player_mapping[player_nr][anim].action;
8818   int direction = player_mapping[player_nr][anim].direction;
8819   int graphic = (direction == MV_NONE ?
8820                  el_act2img(element, action) :
8821                  el_act_dir2img(element, action, direction));
8822   struct GraphicInfo *g = &graphic_info[graphic];
8823   int sync_frame;
8824
8825   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8826
8827   stored_player[player_nr].StepFrame = frame_em;
8828
8829   sync_frame = stored_player[player_nr].Frame;
8830
8831   int frame = getAnimationFrame(g->anim_frames,
8832                                 g->anim_delay,
8833                                 g->anim_mode,
8834                                 g->anim_start_frame,
8835                                 sync_frame);
8836
8837   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8838                       &g_em->src_x, &g_em->src_y, FALSE);
8839 }
8840
8841 void InitGraphicInfo_EM(void)
8842 {
8843   int i, j, p;
8844
8845   // always start with reliable default values
8846   for (i = 0; i < GAME_TILE_MAX; i++)
8847   {
8848     object_mapping[i].element_rnd = EL_UNKNOWN;
8849     object_mapping[i].is_backside = FALSE;
8850     object_mapping[i].action = ACTION_DEFAULT;
8851     object_mapping[i].direction = MV_NONE;
8852   }
8853
8854   // always start with reliable default values
8855   for (p = 0; p < MAX_PLAYERS; p++)
8856   {
8857     for (i = 0; i < PLY_MAX; i++)
8858     {
8859       player_mapping[p][i].element_rnd = EL_UNKNOWN;
8860       player_mapping[p][i].action = ACTION_DEFAULT;
8861       player_mapping[p][i].direction = MV_NONE;
8862     }
8863   }
8864
8865   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8866   {
8867     int e = em_object_mapping_list[i].element_em;
8868
8869     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8870     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8871
8872     if (em_object_mapping_list[i].action != -1)
8873       object_mapping[e].action = em_object_mapping_list[i].action;
8874
8875     if (em_object_mapping_list[i].direction != -1)
8876       object_mapping[e].direction =
8877         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8878   }
8879
8880   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8881   {
8882     int a = em_player_mapping_list[i].action_em;
8883     int p = em_player_mapping_list[i].player_nr;
8884
8885     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8886
8887     if (em_player_mapping_list[i].action != -1)
8888       player_mapping[p][a].action = em_player_mapping_list[i].action;
8889
8890     if (em_player_mapping_list[i].direction != -1)
8891       player_mapping[p][a].direction =
8892         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8893   }
8894
8895   for (i = 0; i < GAME_TILE_MAX; i++)
8896   {
8897     int element = object_mapping[i].element_rnd;
8898     int action = object_mapping[i].action;
8899     int direction = object_mapping[i].direction;
8900     boolean is_backside = object_mapping[i].is_backside;
8901     boolean action_exploding = ((action == ACTION_EXPLODING ||
8902                                  action == ACTION_SMASHED_BY_ROCK ||
8903                                  action == ACTION_SMASHED_BY_SPRING) &&
8904                                 element != EL_DIAMOND);
8905     boolean action_active = (action == ACTION_ACTIVE);
8906     boolean action_other = (action == ACTION_OTHER);
8907
8908     for (j = 0; j < 8; j++)
8909     {
8910       int effective_element = get_effective_element_EM(i, j);
8911       int effective_action = (j < 7 ? action :
8912                               i == Xdrip_stretch ? action :
8913                               i == Xdrip_stretchB ? action :
8914                               i == Ydrip_1_s ? action :
8915                               i == Ydrip_1_sB ? action :
8916                               i == Yball_1 ? action :
8917                               i == Xball_2 ? action :
8918                               i == Yball_2 ? action :
8919                               i == Yball_blank ? action :
8920                               i == Ykey_1_blank ? action :
8921                               i == Ykey_2_blank ? action :
8922                               i == Ykey_3_blank ? action :
8923                               i == Ykey_4_blank ? action :
8924                               i == Ykey_5_blank ? action :
8925                               i == Ykey_6_blank ? action :
8926                               i == Ykey_7_blank ? action :
8927                               i == Ykey_8_blank ? action :
8928                               i == Ylenses_blank ? action :
8929                               i == Ymagnify_blank ? action :
8930                               i == Ygrass_blank ? action :
8931                               i == Ydirt_blank ? action :
8932                               i == Xsand_stonein_1 ? action :
8933                               i == Xsand_stonein_2 ? action :
8934                               i == Xsand_stonein_3 ? action :
8935                               i == Xsand_stonein_4 ? action :
8936                               i == Xsand_stoneout_1 ? action :
8937                               i == Xsand_stoneout_2 ? action :
8938                               i == Xboom_android ? ACTION_EXPLODING :
8939                               action_exploding ? ACTION_EXPLODING :
8940                               action_active ? action :
8941                               action_other ? action :
8942                               ACTION_DEFAULT);
8943       int graphic = (el_act_dir2img(effective_element, effective_action,
8944                                     direction));
8945       int crumbled = (el_act_dir2crm(effective_element, effective_action,
8946                                      direction));
8947       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8948       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8949       boolean has_action_graphics = (graphic != base_graphic);
8950       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8951       struct GraphicInfo *g = &graphic_info[graphic];
8952       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8953       Bitmap *src_bitmap;
8954       int src_x, src_y;
8955       // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8956       boolean special_animation = (action != ACTION_DEFAULT &&
8957                                    g->anim_frames == 3 &&
8958                                    g->anim_delay == 2 &&
8959                                    g->anim_mode & ANIM_LINEAR);
8960       int sync_frame = (i == Xdrip_stretch ? 7 :
8961                         i == Xdrip_stretchB ? 7 :
8962                         i == Ydrip_2_s ? j + 8 :
8963                         i == Ydrip_2_sB ? j + 8 :
8964                         i == Xacid_1 ? 0 :
8965                         i == Xacid_2 ? 10 :
8966                         i == Xacid_3 ? 20 :
8967                         i == Xacid_4 ? 30 :
8968                         i == Xacid_5 ? 40 :
8969                         i == Xacid_6 ? 50 :
8970                         i == Xacid_7 ? 60 :
8971                         i == Xacid_8 ? 70 :
8972                         i == Xfake_acid_1 ? 0 :
8973                         i == Xfake_acid_2 ? 10 :
8974                         i == Xfake_acid_3 ? 20 :
8975                         i == Xfake_acid_4 ? 30 :
8976                         i == Xfake_acid_5 ? 40 :
8977                         i == Xfake_acid_6 ? 50 :
8978                         i == Xfake_acid_7 ? 60 :
8979                         i == Xfake_acid_8 ? 70 :
8980                         i == Xfake_acid_1_player ? 0 :
8981                         i == Xfake_acid_2_player ? 10 :
8982                         i == Xfake_acid_3_player ? 20 :
8983                         i == Xfake_acid_4_player ? 30 :
8984                         i == Xfake_acid_5_player ? 40 :
8985                         i == Xfake_acid_6_player ? 50 :
8986                         i == Xfake_acid_7_player ? 60 :
8987                         i == Xfake_acid_8_player ? 70 :
8988                         i == Xball_2 ? 7 :
8989                         i == Yball_2 ? j + 8 :
8990                         i == Yball_blank ? j + 1 :
8991                         i == Ykey_1_blank ? j + 1 :
8992                         i == Ykey_2_blank ? j + 1 :
8993                         i == Ykey_3_blank ? j + 1 :
8994                         i == Ykey_4_blank ? j + 1 :
8995                         i == Ykey_5_blank ? j + 1 :
8996                         i == Ykey_6_blank ? j + 1 :
8997                         i == Ykey_7_blank ? j + 1 :
8998                         i == Ykey_8_blank ? j + 1 :
8999                         i == Ylenses_blank ? j + 1 :
9000                         i == Ymagnify_blank ? j + 1 :
9001                         i == Ygrass_blank ? j + 1 :
9002                         i == Ydirt_blank ? j + 1 :
9003                         i == Xamoeba_1 ? 0 :
9004                         i == Xamoeba_2 ? 1 :
9005                         i == Xamoeba_3 ? 2 :
9006                         i == Xamoeba_4 ? 3 :
9007                         i == Xamoeba_5 ? 0 :
9008                         i == Xamoeba_6 ? 1 :
9009                         i == Xamoeba_7 ? 2 :
9010                         i == Xamoeba_8 ? 3 :
9011                         i == Xexit_2 ? j + 8 :
9012                         i == Xexit_3 ? j + 16 :
9013                         i == Xdynamite_1 ? 0 :
9014                         i == Xdynamite_2 ? 8 :
9015                         i == Xdynamite_3 ? 16 :
9016                         i == Xdynamite_4 ? 24 :
9017                         i == Xsand_stonein_1 ? j + 1 :
9018                         i == Xsand_stonein_2 ? j + 9 :
9019                         i == Xsand_stonein_3 ? j + 17 :
9020                         i == Xsand_stonein_4 ? j + 25 :
9021                         i == Xsand_stoneout_1 && j == 0 ? 0 :
9022                         i == Xsand_stoneout_1 && j == 1 ? 0 :
9023                         i == Xsand_stoneout_1 && j == 2 ? 1 :
9024                         i == Xsand_stoneout_1 && j == 3 ? 2 :
9025                         i == Xsand_stoneout_1 && j == 4 ? 2 :
9026                         i == Xsand_stoneout_1 && j == 5 ? 3 :
9027                         i == Xsand_stoneout_1 && j == 6 ? 4 :
9028                         i == Xsand_stoneout_1 && j == 7 ? 4 :
9029                         i == Xsand_stoneout_2 && j == 0 ? 5 :
9030                         i == Xsand_stoneout_2 && j == 1 ? 6 :
9031                         i == Xsand_stoneout_2 && j == 2 ? 7 :
9032                         i == Xsand_stoneout_2 && j == 3 ? 8 :
9033                         i == Xsand_stoneout_2 && j == 4 ? 9 :
9034                         i == Xsand_stoneout_2 && j == 5 ? 11 :
9035                         i == Xsand_stoneout_2 && j == 6 ? 13 :
9036                         i == Xsand_stoneout_2 && j == 7 ? 15 :
9037                         i == Xboom_bug && j == 1 ? 2 :
9038                         i == Xboom_bug && j == 2 ? 2 :
9039                         i == Xboom_bug && j == 3 ? 4 :
9040                         i == Xboom_bug && j == 4 ? 4 :
9041                         i == Xboom_bug && j == 5 ? 2 :
9042                         i == Xboom_bug && j == 6 ? 2 :
9043                         i == Xboom_bug && j == 7 ? 0 :
9044                         i == Xboom_tank && j == 1 ? 2 :
9045                         i == Xboom_tank && j == 2 ? 2 :
9046                         i == Xboom_tank && j == 3 ? 4 :
9047                         i == Xboom_tank && j == 4 ? 4 :
9048                         i == Xboom_tank && j == 5 ? 2 :
9049                         i == Xboom_tank && j == 6 ? 2 :
9050                         i == Xboom_tank && j == 7 ? 0 :
9051                         i == Xboom_android && j == 7 ? 6 :
9052                         i == Xboom_1 && j == 1 ? 2 :
9053                         i == Xboom_1 && j == 2 ? 2 :
9054                         i == Xboom_1 && j == 3 ? 4 :
9055                         i == Xboom_1 && j == 4 ? 4 :
9056                         i == Xboom_1 && j == 5 ? 6 :
9057                         i == Xboom_1 && j == 6 ? 6 :
9058                         i == Xboom_1 && j == 7 ? 8 :
9059                         i == Xboom_2 && j == 0 ? 8 :
9060                         i == Xboom_2 && j == 1 ? 8 :
9061                         i == Xboom_2 && j == 2 ? 10 :
9062                         i == Xboom_2 && j == 3 ? 10 :
9063                         i == Xboom_2 && j == 4 ? 10 :
9064                         i == Xboom_2 && j == 5 ? 12 :
9065                         i == Xboom_2 && j == 6 ? 12 :
9066                         i == Xboom_2 && j == 7 ? 12 :
9067                         special_animation && j == 4 ? 3 :
9068                         effective_action != action ? 0 :
9069                         j);
9070       int frame = getAnimationFrame(g->anim_frames,
9071                                     g->anim_delay,
9072                                     g->anim_mode,
9073                                     g->anim_start_frame,
9074                                     sync_frame);
9075
9076       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9077                           g->double_movement && is_backside);
9078
9079       g_em->bitmap = src_bitmap;
9080       g_em->src_x = src_x;
9081       g_em->src_y = src_y;
9082       g_em->src_offset_x = 0;
9083       g_em->src_offset_y = 0;
9084       g_em->dst_offset_x = 0;
9085       g_em->dst_offset_y = 0;
9086       g_em->width  = TILEX;
9087       g_em->height = TILEY;
9088
9089       g_em->preserve_background = FALSE;
9090
9091       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9092                                sync_frame);
9093
9094       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9095                                    effective_action == ACTION_MOVING  ||
9096                                    effective_action == ACTION_PUSHING ||
9097                                    effective_action == ACTION_EATING)) ||
9098           (!has_action_graphics && (effective_action == ACTION_FILLING ||
9099                                     effective_action == ACTION_EMPTYING)))
9100       {
9101         int move_dir =
9102           (effective_action == ACTION_FALLING ||
9103            effective_action == ACTION_FILLING ||
9104            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9105         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9106         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
9107         int num_steps = (i == Ydrip_1_s  ? 16 :
9108                          i == Ydrip_1_sB ? 16 :
9109                          i == Ydrip_2_s  ? 16 :
9110                          i == Ydrip_2_sB ? 16 :
9111                          i == Xsand_stonein_1 ? 32 :
9112                          i == Xsand_stonein_2 ? 32 :
9113                          i == Xsand_stonein_3 ? 32 :
9114                          i == Xsand_stonein_4 ? 32 :
9115                          i == Xsand_stoneout_1 ? 16 :
9116                          i == Xsand_stoneout_2 ? 16 : 8);
9117         int cx = ABS(dx) * (TILEX / num_steps);
9118         int cy = ABS(dy) * (TILEY / num_steps);
9119         int step_frame = (i == Ydrip_2_s        ? j + 8 :
9120                           i == Ydrip_2_sB       ? j + 8 :
9121                           i == Xsand_stonein_2  ? j + 8 :
9122                           i == Xsand_stonein_3  ? j + 16 :
9123                           i == Xsand_stonein_4  ? j + 24 :
9124                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9125         int step = (is_backside ? step_frame : num_steps - step_frame);
9126
9127         if (is_backside)        // tile where movement starts
9128         {
9129           if (dx < 0 || dy < 0)
9130           {
9131             g_em->src_offset_x = cx * step;
9132             g_em->src_offset_y = cy * step;
9133           }
9134           else
9135           {
9136             g_em->dst_offset_x = cx * step;
9137             g_em->dst_offset_y = cy * step;
9138           }
9139         }
9140         else                    // tile where movement ends
9141         {
9142           if (dx < 0 || dy < 0)
9143           {
9144             g_em->dst_offset_x = cx * step;
9145             g_em->dst_offset_y = cy * step;
9146           }
9147           else
9148           {
9149             g_em->src_offset_x = cx * step;
9150             g_em->src_offset_y = cy * step;
9151           }
9152         }
9153
9154         g_em->width  = TILEX - cx * step;
9155         g_em->height = TILEY - cy * step;
9156       }
9157
9158       // create unique graphic identifier to decide if tile must be redrawn
9159       /* bit 31 - 16 (16 bit): EM style graphic
9160          bit 15 - 12 ( 4 bit): EM style frame
9161          bit 11 -  6 ( 6 bit): graphic width
9162          bit  5 -  0 ( 6 bit): graphic height */
9163       g_em->unique_identifier =
9164         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9165     }
9166   }
9167
9168   for (i = 0; i < GAME_TILE_MAX; i++)
9169   {
9170     for (j = 0; j < 8; j++)
9171     {
9172       int element = object_mapping[i].element_rnd;
9173       int action = object_mapping[i].action;
9174       int direction = object_mapping[i].direction;
9175       boolean is_backside = object_mapping[i].is_backside;
9176       int graphic_action  = el_act_dir2img(element, action, direction);
9177       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9178
9179       if ((action == ACTION_SMASHED_BY_ROCK ||
9180            action == ACTION_SMASHED_BY_SPRING ||
9181            action == ACTION_EATING) &&
9182           graphic_action == graphic_default)
9183       {
9184         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
9185                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9186                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
9187                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9188                  Xspring);
9189
9190         // no separate animation for "smashed by rock" -- use rock instead
9191         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9192         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9193
9194         g_em->bitmap            = g_xx->bitmap;
9195         g_em->src_x             = g_xx->src_x;
9196         g_em->src_y             = g_xx->src_y;
9197         g_em->src_offset_x      = g_xx->src_offset_x;
9198         g_em->src_offset_y      = g_xx->src_offset_y;
9199         g_em->dst_offset_x      = g_xx->dst_offset_x;
9200         g_em->dst_offset_y      = g_xx->dst_offset_y;
9201         g_em->width             = g_xx->width;
9202         g_em->height            = g_xx->height;
9203         g_em->unique_identifier = g_xx->unique_identifier;
9204
9205         if (!is_backside)
9206           g_em->preserve_background = TRUE;
9207       }
9208     }
9209   }
9210
9211   for (p = 0; p < MAX_PLAYERS; p++)
9212   {
9213     for (i = 0; i < PLY_MAX; i++)
9214     {
9215       int element = player_mapping[p][i].element_rnd;
9216       int action = player_mapping[p][i].action;
9217       int direction = player_mapping[p][i].direction;
9218
9219       for (j = 0; j < 8; j++)
9220       {
9221         int effective_element = element;
9222         int effective_action = action;
9223         int graphic = (direction == MV_NONE ?
9224                        el_act2img(effective_element, effective_action) :
9225                        el_act_dir2img(effective_element, effective_action,
9226                                       direction));
9227         struct GraphicInfo *g = &graphic_info[graphic];
9228         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9229         Bitmap *src_bitmap;
9230         int src_x, src_y;
9231         int sync_frame = j;
9232         int frame = getAnimationFrame(g->anim_frames,
9233                                       g->anim_delay,
9234                                       g->anim_mode,
9235                                       g->anim_start_frame,
9236                                       sync_frame);
9237
9238         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9239
9240         g_em->bitmap = src_bitmap;
9241         g_em->src_x = src_x;
9242         g_em->src_y = src_y;
9243         g_em->src_offset_x = 0;
9244         g_em->src_offset_y = 0;
9245         g_em->dst_offset_x = 0;
9246         g_em->dst_offset_y = 0;
9247         g_em->width  = TILEX;
9248         g_em->height = TILEY;
9249       }
9250     }
9251   }
9252 }
9253
9254 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9255                                        boolean any_player_moving,
9256                                        boolean any_player_snapping,
9257                                        boolean any_player_dropping)
9258 {
9259   if (frame == 7 && !any_player_dropping)
9260   {
9261     if (!local_player->was_waiting)
9262     {
9263       if (!CheckSaveEngineSnapshotToList())
9264         return;
9265
9266       local_player->was_waiting = TRUE;
9267     }
9268   }
9269   else if (any_player_moving || any_player_snapping || any_player_dropping)
9270   {
9271     local_player->was_waiting = FALSE;
9272   }
9273 }
9274
9275 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9276                                        boolean murphy_is_dropping)
9277 {
9278   if (murphy_is_waiting)
9279   {
9280     if (!local_player->was_waiting)
9281     {
9282       if (!CheckSaveEngineSnapshotToList())
9283         return;
9284
9285       local_player->was_waiting = TRUE;
9286     }
9287   }
9288   else
9289   {
9290     local_player->was_waiting = FALSE;
9291   }
9292 }
9293
9294 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9295                                        boolean button_released)
9296 {
9297   if (button_released)
9298   {
9299     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9300       CheckSaveEngineSnapshotToList();
9301   }
9302   else if (element_clicked)
9303   {
9304     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9305       CheckSaveEngineSnapshotToList();
9306
9307     game.snapshot.changed_action = TRUE;
9308   }
9309 }
9310
9311 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9312                                boolean any_player_moving,
9313                                boolean any_player_snapping,
9314                                boolean any_player_dropping)
9315 {
9316   if (tape.single_step && tape.recording && !tape.pausing)
9317     if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9318       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9319
9320   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9321                              any_player_snapping, any_player_dropping);
9322
9323   return tape.pausing;
9324 }
9325
9326 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9327                             boolean murphy_is_dropping)
9328 {
9329   boolean murphy_starts_dropping = FALSE;
9330   int i;
9331
9332   for (i = 0; i < MAX_PLAYERS; i++)
9333     if (stored_player[i].force_dropping)
9334       murphy_starts_dropping = TRUE;
9335
9336   if (tape.single_step && tape.recording && !tape.pausing)
9337     if (murphy_is_waiting && !murphy_starts_dropping)
9338       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9339
9340   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9341 }
9342
9343 void CheckSingleStepMode_MM(boolean element_clicked,
9344                             boolean button_released)
9345 {
9346   if (tape.single_step && tape.recording && !tape.pausing)
9347     if (button_released)
9348       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9349
9350   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9351 }
9352
9353 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9354                          int graphic, int sync_frame, int x, int y)
9355 {
9356   int frame = getGraphicAnimationFrame(graphic, sync_frame);
9357
9358   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9359 }
9360
9361 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9362 {
9363   return (IS_NEXT_FRAME(sync_frame, graphic));
9364 }
9365
9366 int getGraphicInfo_Delay(int graphic)
9367 {
9368   return graphic_info[graphic].anim_delay;
9369 }
9370
9371 void PlayMenuSoundExt(int sound)
9372 {
9373   if (sound == SND_UNDEFINED)
9374     return;
9375
9376   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9377       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9378     return;
9379
9380   if (IS_LOOP_SOUND(sound))
9381     PlaySoundLoop(sound);
9382   else
9383     PlaySound(sound);
9384 }
9385
9386 void PlayMenuSound(void)
9387 {
9388   PlayMenuSoundExt(menu.sound[game_status]);
9389 }
9390
9391 void PlayMenuSoundStereo(int sound, int stereo_position)
9392 {
9393   if (sound == SND_UNDEFINED)
9394     return;
9395
9396   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9397       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9398     return;
9399
9400   if (IS_LOOP_SOUND(sound))
9401     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9402   else
9403     PlaySoundStereo(sound, stereo_position);
9404 }
9405
9406 void PlayMenuSoundIfLoopExt(int sound)
9407 {
9408   if (sound == SND_UNDEFINED)
9409     return;
9410
9411   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9412       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9413     return;
9414
9415   if (IS_LOOP_SOUND(sound))
9416     PlaySoundLoop(sound);
9417 }
9418
9419 void PlayMenuSoundIfLoop(void)
9420 {
9421   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9422 }
9423
9424 void PlayMenuMusicExt(int music)
9425 {
9426   if (music == MUS_UNDEFINED)
9427     return;
9428
9429   if (!setup.sound_music)
9430     return;
9431
9432   if (IS_LOOP_MUSIC(music))
9433     PlayMusicLoop(music);
9434   else
9435     PlayMusic(music);
9436 }
9437
9438 void PlayMenuMusic(void)
9439 {
9440   char *curr_music = getCurrentlyPlayingMusicFilename();
9441   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9442
9443   if (!strEqual(curr_music, next_music))
9444     PlayMenuMusicExt(menu.music[game_status]);
9445 }
9446
9447 void PlayMenuSoundsAndMusic(void)
9448 {
9449   PlayMenuSound();
9450   PlayMenuMusic();
9451 }
9452
9453 static void FadeMenuSounds(void)
9454 {
9455   FadeSounds();
9456 }
9457
9458 static void FadeMenuMusic(void)
9459 {
9460   char *curr_music = getCurrentlyPlayingMusicFilename();
9461   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9462
9463   if (!strEqual(curr_music, next_music))
9464     FadeMusic();
9465 }
9466
9467 void FadeMenuSoundsAndMusic(void)
9468 {
9469   FadeMenuSounds();
9470   FadeMenuMusic();
9471 }
9472
9473 void PlaySoundActivating(void)
9474 {
9475 #if 0
9476   PlaySound(SND_MENU_ITEM_ACTIVATING);
9477 #endif
9478 }
9479
9480 void PlaySoundSelecting(void)
9481 {
9482 #if 0
9483   PlaySound(SND_MENU_ITEM_SELECTING);
9484 #endif
9485 }
9486
9487 void ToggleFullscreenIfNeeded(void)
9488 {
9489   // if setup and video fullscreen state are already matching, nothing do do
9490   if (setup.fullscreen == video.fullscreen_enabled ||
9491       !video.fullscreen_available)
9492     return;
9493
9494   SDLSetWindowFullscreen(setup.fullscreen);
9495
9496   // set setup value according to successfully changed fullscreen mode
9497   setup.fullscreen = video.fullscreen_enabled;
9498 }
9499
9500 void ChangeWindowScalingIfNeeded(void)
9501 {
9502   // if setup and video window scaling are already matching, nothing do do
9503   if (setup.window_scaling_percent == video.window_scaling_percent ||
9504       video.fullscreen_enabled)
9505     return;
9506
9507   SDLSetWindowScaling(setup.window_scaling_percent);
9508
9509   // set setup value according to successfully changed window scaling
9510   setup.window_scaling_percent = video.window_scaling_percent;
9511 }
9512
9513 void ChangeVsyncModeIfNeeded(void)
9514 {
9515   int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9516   int video_vsync_mode = video.vsync_mode;
9517
9518   // if setup and video vsync mode are already matching, nothing do do
9519   if (setup_vsync_mode == video_vsync_mode)
9520     return;
9521
9522   // if renderer is using OpenGL, vsync mode can directly be changed
9523   SDLSetScreenVsyncMode(setup.vsync_mode);
9524
9525   // if vsync mode unchanged, try re-creating renderer to set vsync mode
9526   if (video.vsync_mode == video_vsync_mode)
9527   {
9528     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9529
9530     // save backbuffer content which gets lost when re-creating screen
9531     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9532
9533     // force re-creating screen and renderer to set new vsync mode
9534     video.fullscreen_enabled = !setup.fullscreen;
9535
9536     // when creating new renderer, destroy textures linked to old renderer
9537     FreeAllImageTextures();     // needs old renderer to free the textures
9538
9539     // re-create screen and renderer (including change of vsync mode)
9540     ChangeVideoModeIfNeeded(setup.fullscreen);
9541
9542     // set setup value according to successfully changed fullscreen mode
9543     setup.fullscreen = video.fullscreen_enabled;
9544
9545     // restore backbuffer content from temporary backbuffer backup bitmap
9546     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9547     FreeBitmap(tmp_backbuffer);
9548
9549     // update visible window/screen
9550     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9551
9552     // when changing vsync mode, re-create textures for new renderer
9553     InitImageTextures();
9554   }
9555
9556   // set setup value according to successfully changed vsync mode
9557   setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9558 }
9559
9560 static void JoinRectangles(int *x, int *y, int *width, int *height,
9561                            int x2, int y2, int width2, int height2)
9562 {
9563   // do not join with "off-screen" rectangle
9564   if (x2 == -1 || y2 == -1)
9565     return;
9566
9567   *x = MIN(*x, x2);
9568   *y = MIN(*y, y2);
9569   *width = MAX(*width, width2);
9570   *height = MAX(*height, height2);
9571 }
9572
9573 void SetAnimStatus(int anim_status_new)
9574 {
9575   if (anim_status_new == GAME_MODE_MAIN)
9576     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9577   else if (anim_status_new == GAME_MODE_NAMES)
9578     anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9579   else if (anim_status_new == GAME_MODE_SCORES)
9580     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9581
9582   global.anim_status_next = anim_status_new;
9583
9584   // directly set screen modes that are entered without fading
9585   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
9586        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9587       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
9588        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9589       (global.anim_status      == GAME_MODE_PSEUDO_NAMESONLY &&
9590        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9591       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAMES &&
9592        global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9593     global.anim_status = global.anim_status_next;
9594 }
9595
9596 void SetGameStatus(int game_status_new)
9597 {
9598   if (game_status_new != game_status)
9599     game_status_last_screen = game_status;
9600
9601   game_status = game_status_new;
9602
9603   SetAnimStatus(game_status_new);
9604 }
9605
9606 void SetFontStatus(int game_status_new)
9607 {
9608   static int last_game_status = -1;
9609
9610   if (game_status_new != -1)
9611   {
9612     // set game status for font use after storing last game status
9613     last_game_status = game_status;
9614     game_status = game_status_new;
9615   }
9616   else
9617   {
9618     // reset game status after font use from last stored game status
9619     game_status = last_game_status;
9620   }
9621 }
9622
9623 void ResetFontStatus(void)
9624 {
9625   SetFontStatus(-1);
9626 }
9627
9628 void SetLevelSetInfo(char *identifier, int level_nr)
9629 {
9630   setString(&levelset.identifier, identifier);
9631
9632   levelset.level_nr = level_nr;
9633 }
9634
9635 boolean CheckIfAllViewportsHaveChanged(void)
9636 {
9637   // if game status has not changed, viewports have not changed either
9638   if (game_status == game_status_last)
9639     return FALSE;
9640
9641   // check if all viewports have changed with current game status
9642
9643   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9644   struct RectWithBorder *vp_door_1    = &viewport.door_1[game_status];
9645   struct RectWithBorder *vp_door_2    = &viewport.door_2[game_status];
9646   int new_real_sx       = vp_playfield->x;
9647   int new_real_sy       = vp_playfield->y;
9648   int new_full_sxsize   = vp_playfield->width;
9649   int new_full_sysize   = vp_playfield->height;
9650   int new_dx            = vp_door_1->x;
9651   int new_dy            = vp_door_1->y;
9652   int new_dxsize        = vp_door_1->width;
9653   int new_dysize        = vp_door_1->height;
9654   int new_vx            = vp_door_2->x;
9655   int new_vy            = vp_door_2->y;
9656   int new_vxsize        = vp_door_2->width;
9657   int new_vysize        = vp_door_2->height;
9658
9659   boolean playfield_viewport_has_changed =
9660     (new_real_sx != REAL_SX ||
9661      new_real_sy != REAL_SY ||
9662      new_full_sxsize != FULL_SXSIZE ||
9663      new_full_sysize != FULL_SYSIZE);
9664
9665   boolean door_1_viewport_has_changed =
9666     (new_dx != DX ||
9667      new_dy != DY ||
9668      new_dxsize != DXSIZE ||
9669      new_dysize != DYSIZE);
9670
9671   boolean door_2_viewport_has_changed =
9672     (new_vx != VX ||
9673      new_vy != VY ||
9674      new_vxsize != VXSIZE ||
9675      new_vysize != VYSIZE ||
9676      game_status_last == GAME_MODE_EDITOR);
9677
9678   return (playfield_viewport_has_changed &&
9679           door_1_viewport_has_changed &&
9680           door_2_viewport_has_changed);
9681 }
9682
9683 boolean CheckFadeAll(void)
9684 {
9685   return (CheckIfGlobalBorderHasChanged() ||
9686           CheckIfAllViewportsHaveChanged());
9687 }
9688
9689 void ChangeViewportPropertiesIfNeeded(void)
9690 {
9691   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9692                                FALSE : setup.small_game_graphics);
9693   int gfx_game_mode = game_status;
9694   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9695                         game_status);
9696   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
9697   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9698   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
9699   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
9700   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
9701   int new_win_xsize     = vp_window->width;
9702   int new_win_ysize     = vp_window->height;
9703   int border_left       = vp_playfield->border_left;
9704   int border_right      = vp_playfield->border_right;
9705   int border_top        = vp_playfield->border_top;
9706   int border_bottom     = vp_playfield->border_bottom;
9707   int new_sx            = vp_playfield->x      + border_left;
9708   int new_sy            = vp_playfield->y      + border_top;
9709   int new_sxsize        = vp_playfield->width  - border_left - border_right;
9710   int new_sysize        = vp_playfield->height - border_top  - border_bottom;
9711   int new_real_sx       = vp_playfield->x;
9712   int new_real_sy       = vp_playfield->y;
9713   int new_full_sxsize   = vp_playfield->width;
9714   int new_full_sysize   = vp_playfield->height;
9715   int new_dx            = vp_door_1->x;
9716   int new_dy            = vp_door_1->y;
9717   int new_dxsize        = vp_door_1->width;
9718   int new_dysize        = vp_door_1->height;
9719   int new_vx            = vp_door_2->x;
9720   int new_vy            = vp_door_2->y;
9721   int new_vxsize        = vp_door_2->width;
9722   int new_vysize        = vp_door_2->height;
9723   int new_ex            = vp_door_3->x;
9724   int new_ey            = vp_door_3->y;
9725   int new_exsize        = vp_door_3->width;
9726   int new_eysize        = vp_door_3->height;
9727   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9728   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9729                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9730   int new_scr_fieldx = new_sxsize / tilesize;
9731   int new_scr_fieldy = new_sysize / tilesize;
9732   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9733   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9734   boolean init_gfx_buffers = FALSE;
9735   boolean init_video_buffer = FALSE;
9736   boolean init_gadgets_and_anims = FALSE;
9737   boolean init_em_graphics = FALSE;
9738
9739   if (new_win_xsize != WIN_XSIZE ||
9740       new_win_ysize != WIN_YSIZE)
9741   {
9742     WIN_XSIZE = new_win_xsize;
9743     WIN_YSIZE = new_win_ysize;
9744
9745     init_video_buffer = TRUE;
9746     init_gfx_buffers = TRUE;
9747     init_gadgets_and_anims = TRUE;
9748
9749     // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9750   }
9751
9752   if (new_scr_fieldx != SCR_FIELDX ||
9753       new_scr_fieldy != SCR_FIELDY)
9754   {
9755     // this always toggles between MAIN and GAME when using small tile size
9756
9757     SCR_FIELDX = new_scr_fieldx;
9758     SCR_FIELDY = new_scr_fieldy;
9759
9760     // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9761   }
9762
9763   if (new_sx != SX ||
9764       new_sy != SY ||
9765       new_dx != DX ||
9766       new_dy != DY ||
9767       new_vx != VX ||
9768       new_vy != VY ||
9769       new_ex != EX ||
9770       new_ey != EY ||
9771       new_sxsize != SXSIZE ||
9772       new_sysize != SYSIZE ||
9773       new_dxsize != DXSIZE ||
9774       new_dysize != DYSIZE ||
9775       new_vxsize != VXSIZE ||
9776       new_vysize != VYSIZE ||
9777       new_exsize != EXSIZE ||
9778       new_eysize != EYSIZE ||
9779       new_real_sx != REAL_SX ||
9780       new_real_sy != REAL_SY ||
9781       new_full_sxsize != FULL_SXSIZE ||
9782       new_full_sysize != FULL_SYSIZE ||
9783       new_tilesize_var != TILESIZE_VAR
9784       )
9785   {
9786     // ------------------------------------------------------------------------
9787     // determine next fading area for changed viewport definitions
9788     // ------------------------------------------------------------------------
9789
9790     // start with current playfield area (default fading area)
9791     FADE_SX = REAL_SX;
9792     FADE_SY = REAL_SY;
9793     FADE_SXSIZE = FULL_SXSIZE;
9794     FADE_SYSIZE = FULL_SYSIZE;
9795
9796     // add new playfield area if position or size has changed
9797     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9798         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9799     {
9800       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9801                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9802     }
9803
9804     // add current and new door 1 area if position or size has changed
9805     if (new_dx != DX || new_dy != DY ||
9806         new_dxsize != DXSIZE || new_dysize != DYSIZE)
9807     {
9808       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9809                      DX, DY, DXSIZE, DYSIZE);
9810       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9811                      new_dx, new_dy, new_dxsize, new_dysize);
9812     }
9813
9814     // add current and new door 2 area if position or size has changed
9815     if (new_vx != VX || new_vy != VY ||
9816         new_vxsize != VXSIZE || new_vysize != VYSIZE)
9817     {
9818       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9819                      VX, VY, VXSIZE, VYSIZE);
9820       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9821                      new_vx, new_vy, new_vxsize, new_vysize);
9822     }
9823
9824     // ------------------------------------------------------------------------
9825     // handle changed tile size
9826     // ------------------------------------------------------------------------
9827
9828     if (new_tilesize_var != TILESIZE_VAR)
9829     {
9830       // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9831
9832       // changing tile size invalidates scroll values of engine snapshots
9833       FreeEngineSnapshotSingle();
9834
9835       // changing tile size requires update of graphic mapping for EM engine
9836       init_em_graphics = TRUE;
9837     }
9838
9839     SX = new_sx;
9840     SY = new_sy;
9841     DX = new_dx;
9842     DY = new_dy;
9843     VX = new_vx;
9844     VY = new_vy;
9845     EX = new_ex;
9846     EY = new_ey;
9847     SXSIZE = new_sxsize;
9848     SYSIZE = new_sysize;
9849     DXSIZE = new_dxsize;
9850     DYSIZE = new_dysize;
9851     VXSIZE = new_vxsize;
9852     VYSIZE = new_vysize;
9853     EXSIZE = new_exsize;
9854     EYSIZE = new_eysize;
9855     REAL_SX = new_real_sx;
9856     REAL_SY = new_real_sy;
9857     FULL_SXSIZE = new_full_sxsize;
9858     FULL_SYSIZE = new_full_sysize;
9859     TILESIZE_VAR = new_tilesize_var;
9860
9861     init_gfx_buffers = TRUE;
9862     init_gadgets_and_anims = TRUE;
9863
9864     // Debug("tools:viewport", "viewports: init_gfx_buffers");
9865     // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9866   }
9867
9868   if (init_gfx_buffers)
9869   {
9870     // Debug("tools:viewport", "init_gfx_buffers");
9871
9872     SCR_FIELDX = new_scr_fieldx_buffers;
9873     SCR_FIELDY = new_scr_fieldy_buffers;
9874
9875     InitGfxBuffers();
9876
9877     SCR_FIELDX = new_scr_fieldx;
9878     SCR_FIELDY = new_scr_fieldy;
9879
9880     SetDrawDeactivationMask(REDRAW_NONE);
9881     SetDrawBackgroundMask(REDRAW_FIELD);
9882   }
9883
9884   if (init_video_buffer)
9885   {
9886     // Debug("tools:viewport", "init_video_buffer");
9887
9888     FreeAllImageTextures();     // needs old renderer to free the textures
9889
9890     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9891     InitImageTextures();
9892   }
9893
9894   if (init_gadgets_and_anims)
9895   {
9896     // Debug("tools:viewport", "init_gadgets_and_anims");
9897
9898     InitGadgets();
9899     InitGlobalAnimations();
9900   }
9901
9902   if (init_em_graphics)
9903   {
9904     InitGraphicInfo_EM();
9905   }
9906 }