dc847065c0916b414429cc4aae6af7373c946f88
[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 //                  http://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 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX            FALSE
28 #define DEBUG_FRAME_TIME        FALSE
29
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES        0
32 #define TOOL_CTRL_ID_NO         1
33 #define TOOL_CTRL_ID_CONFIRM    2
34 #define TOOL_CTRL_ID_PLAYER_1   3
35 #define TOOL_CTRL_ID_PLAYER_2   4
36 #define TOOL_CTRL_ID_PLAYER_3   5
37 #define TOOL_CTRL_ID_PLAYER_4   6
38
39 #define NUM_TOOL_BUTTONS        7
40
41 /* constants for number of doors and door parts */
42 #define NUM_DOORS               2
43 #define NUM_PANELS              NUM_DOORS
44 // #define NUM_PANELS           0
45 #define MAX_PARTS_PER_DOOR      8
46 #define MAX_DOOR_PARTS          (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i)   ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
48
49
50 struct DoorPartOrderInfo
51 {
52   int nr;
53   int sort_priority;
54 };
55
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57
58 struct DoorPartControlInfo
59 {
60   int door_token;
61   int graphic;
62   struct DoorPartPosInfo *pos;
63 };
64
65 static struct DoorPartControlInfo door_part_controls[] =
66 {
67   {
68     DOOR_1,
69     IMG_GFX_DOOR_1_PART_1,
70     &door_1.part_1
71   },
72   {
73     DOOR_1,
74     IMG_GFX_DOOR_1_PART_2,
75     &door_1.part_2
76   },
77   {
78     DOOR_1,
79     IMG_GFX_DOOR_1_PART_3,
80     &door_1.part_3
81   },
82   {
83     DOOR_1,
84     IMG_GFX_DOOR_1_PART_4,
85     &door_1.part_4
86   },
87   {
88     DOOR_1,
89     IMG_GFX_DOOR_1_PART_5,
90     &door_1.part_5
91   },
92   {
93     DOOR_1,
94     IMG_GFX_DOOR_1_PART_6,
95     &door_1.part_6
96   },
97   {
98     DOOR_1,
99     IMG_GFX_DOOR_1_PART_7,
100     &door_1.part_7
101   },
102   {
103     DOOR_1,
104     IMG_GFX_DOOR_1_PART_8,
105     &door_1.part_8
106   },
107
108   {
109     DOOR_2,
110     IMG_GFX_DOOR_2_PART_1,
111     &door_2.part_1
112   },
113   {
114     DOOR_2,
115     IMG_GFX_DOOR_2_PART_2,
116     &door_2.part_2
117   },
118   {
119     DOOR_2,
120     IMG_GFX_DOOR_2_PART_3,
121     &door_2.part_3
122   },
123   {
124     DOOR_2,
125     IMG_GFX_DOOR_2_PART_4,
126     &door_2.part_4
127   },
128   {
129     DOOR_2,
130     IMG_GFX_DOOR_2_PART_5,
131     &door_2.part_5
132   },
133   {
134     DOOR_2,
135     IMG_GFX_DOOR_2_PART_6,
136     &door_2.part_6
137   },
138   {
139     DOOR_2,
140     IMG_GFX_DOOR_2_PART_7,
141     &door_2.part_7
142   },
143   {
144     DOOR_2,
145     IMG_GFX_DOOR_2_PART_8,
146     &door_2.part_8
147   },
148
149   {
150     DOOR_1,
151     IMG_BACKGROUND_PANEL,
152     &door_1.panel
153   },
154   {
155     DOOR_2,
156     IMG_BACKGROUND_TAPE,
157     &door_2.panel
158   },
159
160   {
161     -1,
162     -1,
163     NULL
164   }
165 };
166
167
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
173
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
176
177 static char *print_if_not_empty(int element)
178 {
179   static char *s = NULL;
180   char *token_name = element_info[element].token_name;
181
182   if (s != NULL)
183     free(s);
184
185   s = checked_malloc(strlen(token_name) + 10 + 1);
186
187   if (element != EL_EMPTY)
188     sprintf(s, "%d\t['%s']", element, token_name);
189   else
190     sprintf(s, "%d", element);
191
192   return s;
193 }
194
195 int correctLevelPosX_EM(int lx)
196 {
197   lx -= 1;
198   lx -= (BorderElement != EL_EMPTY ? 1 : 0);
199
200   return lx;
201 }
202
203 int correctLevelPosY_EM(int ly)
204 {
205   ly -= 1;
206   ly -= (BorderElement != EL_EMPTY ? 1 : 0);
207
208   return ly;
209 }
210
211 static int getFieldbufferOffsetX_RND()
212 {
213   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214   int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215   int dx_var = dx * TILESIZE_VAR / TILESIZE;
216   int fx = FX;
217
218   if (EVEN(SCR_FIELDX))
219   {
220     int ffx = (scroll_x - SBX_Left)  * TILEX_VAR + dx_var;
221
222     if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223       fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
224     else
225       fx += (dx_var > 0 ? TILEX_VAR : 0);
226   }
227   else
228   {
229     fx += dx_var;
230   }
231
232   if (full_lev_fieldx <= SCR_FIELDX)
233   {
234     if (EVEN(SCR_FIELDX))
235       fx = 2 * TILEX_VAR - (ODD(lev_fieldx)  ? TILEX_VAR / 2 : 0);
236     else
237       fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
238   }
239
240   return fx;
241 }
242
243 static int getFieldbufferOffsetY_RND()
244 {
245   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246   int dy = (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
247   int dy_var = dy * TILESIZE_VAR / TILESIZE;
248   int fy = FY;
249
250   if (EVEN(SCR_FIELDY))
251   {
252     int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
253
254     if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255       fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
256     else
257       fy += (dy_var > 0 ? TILEY_VAR : 0);
258   }
259   else
260   {
261     fy += dy_var;
262   }
263
264   if (full_lev_fieldy <= SCR_FIELDY)
265   {
266     if (EVEN(SCR_FIELDY))
267       fy = 2 * TILEY_VAR - (ODD(lev_fieldy)  ? TILEY_VAR / 2 : 0);
268     else
269       fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
270   }
271
272   return fy;
273 }
274
275 static int getLevelFromScreenX_RND(int sx)
276 {
277   int fx = getFieldbufferOffsetX_RND();
278   int dx = fx - FX;
279   int px = sx - SX;
280   int lx = LEVELX((px + dx) / TILESIZE_VAR);
281
282   return lx;
283 }
284
285 static int getLevelFromScreenY_RND(int sy)
286 {
287   int fy = getFieldbufferOffsetY_RND();
288   int dy = fy - FY;
289   int py = sy - SY;
290   int ly = LEVELY((py + dy) / TILESIZE_VAR);
291
292   return ly;
293 }
294
295 static int getLevelFromScreenX_EM(int sx)
296 {
297   int level_xsize = level.native_em_level->lev->width;
298   int full_xsize = level_xsize * TILESIZE_VAR;
299
300   sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
301
302   int fx = getFieldbufferOffsetX_EM();
303   int dx = fx;
304   int px = sx - SX;
305   int lx = LEVELX((px + dx) / TILESIZE_VAR);
306
307   lx = correctLevelPosX_EM(lx);
308
309   return lx;
310 }
311
312 static int getLevelFromScreenY_EM(int sy)
313 {
314   int level_ysize = level.native_em_level->lev->height;
315   int full_ysize = level_ysize * TILESIZE_VAR;
316
317   sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
318
319   int fy = getFieldbufferOffsetY_EM();
320   int dy = fy;
321   int py = sy - SY;
322   int ly = LEVELY((py + dy) / TILESIZE_VAR);
323
324   ly = correctLevelPosY_EM(ly);
325
326   return ly;
327 }
328
329 static int getLevelFromScreenX_SP(int sx)
330 {
331   int menBorder = setup.sp_show_border_elements;
332   int level_xsize = level.native_sp_level->width;
333   int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334
335   sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
336
337   int fx = getFieldbufferOffsetX_SP();
338   int dx = fx - FX;
339   int px = sx - SX;
340   int lx = LEVELX((px + dx) / TILESIZE_VAR);
341
342   return lx;
343 }
344
345 static int getLevelFromScreenY_SP(int sy)
346 {
347   int menBorder = setup.sp_show_border_elements;
348   int level_ysize = level.native_sp_level->height;
349   int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
350
351   sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
352
353   int fy = getFieldbufferOffsetY_SP();
354   int dy = fy - FY;
355   int py = sy - SY;
356   int ly = LEVELY((py + dy) / TILESIZE_VAR);
357
358   return ly;
359 }
360
361 static int getLevelFromScreenX_MM(int sx)
362 {
363 #if 0
364   int level_xsize = level.native_mm_level->fieldx;
365   int full_xsize = level_xsize * TILESIZE_VAR;
366
367   sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
368 #endif
369
370   int px = sx - SX;
371   int lx = px / TILESIZE_VAR;
372
373   return lx;
374 }
375
376 static int getLevelFromScreenY_MM(int sy)
377 {
378 #if 0
379   int level_ysize = level.native_mm_level->fieldy;
380   int full_ysize = level_ysize * TILESIZE_VAR;
381
382   sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
383 #endif
384
385   int py = sy - SY;
386   int ly = py / TILESIZE_VAR;
387
388   return ly;
389 }
390
391 int getLevelFromScreenX(int x)
392 {
393   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394     return getLevelFromScreenX_EM(x);
395   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396     return getLevelFromScreenX_SP(x);
397   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398     return getLevelFromScreenX_MM(x);
399   else
400     return getLevelFromScreenX_RND(x);
401 }
402
403 int getLevelFromScreenY(int y)
404 {
405   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
406     return getLevelFromScreenY_EM(y);
407   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
408     return getLevelFromScreenY_SP(y);
409   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
410     return getLevelFromScreenY_MM(y);
411   else
412     return getLevelFromScreenY_RND(y);
413 }
414
415 void DumpTile(int x, int y)
416 {
417   int sx = SCREENX(x);
418   int sy = SCREENY(y);
419   char *token_name;
420
421   printf_line("-", 79);
422   printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
423   printf_line("-", 79);
424
425   if (!IN_LEV_FIELD(x, y))
426   {
427     printf("(not in level field)\n");
428     printf("\n");
429
430     return;
431   }
432
433   token_name = element_info[Feld[x][y]].token_name;
434
435   printf("  Feld:        %d\t['%s']\n", Feld[x][y], token_name);
436   printf("  Back:        %s\n", print_if_not_empty(Back[x][y]));
437   printf("  Store:       %s\n", print_if_not_empty(Store[x][y]));
438   printf("  Store2:      %s\n", print_if_not_empty(Store2[x][y]));
439   printf("  StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
440   printf("  MovPos:      %d\n", MovPos[x][y]);
441   printf("  MovDir:      %d\n", MovDir[x][y]);
442   printf("  MovDelay:    %d\n", MovDelay[x][y]);
443   printf("  ChangeDelay: %d\n", ChangeDelay[x][y]);
444   printf("  CustomValue: %d\n", CustomValue[x][y]);
445   printf("  GfxElement:  %d\n", GfxElement[x][y]);
446   printf("  GfxAction:   %d\n", GfxAction[x][y]);
447   printf("  GfxFrame:    %d [%d]\n", GfxFrame[x][y], FrameCounter);
448   printf("  Player x/y:  %d, %d\n", local_player->jx, local_player->jy);
449   printf("\n");
450 }
451
452 void DumpTileFromScreen(int sx, int sy)
453 {
454   int lx = getLevelFromScreenX(sx);
455   int ly = getLevelFromScreenY(sy);
456
457   DumpTile(lx, ly);
458 }
459
460 void SetDrawtoField(int mode)
461 {
462   if (mode == DRAW_TO_FIELDBUFFER)
463   {
464     FX = 2 * TILEX_VAR;
465     FY = 2 * TILEY_VAR;
466     BX1 = -2;
467     BY1 = -2;
468     BX2 = SCR_FIELDX + 1;
469     BY2 = SCR_FIELDY + 1;
470
471     drawto_field = fieldbuffer;
472   }
473   else  /* DRAW_TO_BACKBUFFER */
474   {
475     FX = SX;
476     FY = SY;
477     BX1 = 0;
478     BY1 = 0;
479     BX2 = SCR_FIELDX - 1;
480     BY2 = SCR_FIELDY - 1;
481
482     drawto_field = backbuffer;
483   }
484 }
485
486 static void RedrawPlayfield_RND()
487 {
488   if (game.envelope_active)
489     return;
490
491   DrawLevel(REDRAW_ALL);
492   DrawAllPlayers();
493 }
494
495 void RedrawPlayfield()
496 {
497   if (game_status != GAME_MODE_PLAYING)
498     return;
499
500   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
501     RedrawPlayfield_EM(TRUE);
502   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
503     RedrawPlayfield_SP(TRUE);
504   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
505     RedrawPlayfield_MM();
506   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
507     RedrawPlayfield_RND();
508
509   BlitScreenToBitmap(backbuffer);
510
511   BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
512              gfx.sx, gfx.sy);
513 }
514
515 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
516                                      int draw_target)
517 {
518   Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
519   Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
520
521   if (x == -1 && y == -1)
522     return;
523
524   if (draw_target == DRAW_TO_SCREEN)
525     BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
526   else
527     BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
528 }
529
530 static void DrawMaskedBorderExt_FIELD(int draw_target)
531 {
532   if (global.border_status >= GAME_MODE_MAIN &&
533       global.border_status <= GAME_MODE_PLAYING &&
534       border.draw_masked[global.border_status])
535     DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
536                              draw_target);
537 }
538
539 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
540 {
541   // when drawing to backbuffer, never draw border over open doors
542   if (draw_target == DRAW_TO_BACKBUFFER &&
543       (GetDoorState() & DOOR_OPEN_1))
544     return;
545
546   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
547       (global.border_status != GAME_MODE_EDITOR ||
548        border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
549     DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
550 }
551
552 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
553 {
554   // when drawing to backbuffer, never draw border over open doors
555   if (draw_target == DRAW_TO_BACKBUFFER &&
556       (GetDoorState() & DOOR_OPEN_2))
557     return;
558
559   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
560       global.border_status != GAME_MODE_EDITOR)
561     DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
562 }
563
564 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
565 {
566   /* currently not available */
567 }
568
569 static void DrawMaskedBorderExt_ALL(int draw_target)
570 {
571   DrawMaskedBorderExt_FIELD(draw_target);
572   DrawMaskedBorderExt_DOOR_1(draw_target);
573   DrawMaskedBorderExt_DOOR_2(draw_target);
574   DrawMaskedBorderExt_DOOR_3(draw_target);
575 }
576
577 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
578 {
579   /* never draw masked screen borders on borderless screens */
580   if (global.border_status == GAME_MODE_LOADING ||
581       global.border_status == GAME_MODE_TITLE)
582     return;
583
584   if (redraw_mask & REDRAW_ALL)
585     DrawMaskedBorderExt_ALL(draw_target);
586   else
587   {
588     if (redraw_mask & REDRAW_FIELD)
589       DrawMaskedBorderExt_FIELD(draw_target);
590     if (redraw_mask & REDRAW_DOOR_1)
591       DrawMaskedBorderExt_DOOR_1(draw_target);
592     if (redraw_mask & REDRAW_DOOR_2)
593       DrawMaskedBorderExt_DOOR_2(draw_target);
594     if (redraw_mask & REDRAW_DOOR_3)
595       DrawMaskedBorderExt_DOOR_3(draw_target);
596   }
597 }
598
599 void DrawMaskedBorder_FIELD()
600 {
601   DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
602 }
603
604 void DrawMaskedBorder(int redraw_mask)
605 {
606   DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
607 }
608
609 void DrawMaskedBorderToTarget(int draw_target)
610 {
611   if (draw_target == DRAW_TO_BACKBUFFER ||
612       draw_target == DRAW_TO_SCREEN)
613   {
614     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
615   }
616   else
617   {
618     int last_border_status = global.border_status;
619
620     if (draw_target == DRAW_TO_FADE_SOURCE)
621     {
622       global.border_status = gfx.fade_border_source_status;
623       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
624     }
625     else if (draw_target == DRAW_TO_FADE_TARGET)
626     {
627       global.border_status = gfx.fade_border_target_status;
628       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
629     }
630
631     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
632
633     global.border_status = last_border_status;
634     gfx.masked_border_bitmap_ptr = backbuffer;
635   }
636 }
637
638 void DrawTileCursor(int draw_target)
639 {
640   Bitmap *fade_bitmap;
641   Bitmap *src_bitmap;
642   int src_x, src_y;
643   int dst_x, dst_y;
644   int graphic = IMG_GLOBAL_TILE_CURSOR;
645   int frame = 0;
646   int tilesize = TILESIZE_VAR;
647   int width = tilesize;
648   int height = tilesize;
649
650   if (game_status != GAME_MODE_PLAYING)
651     return;
652
653   if (!tile_cursor.enabled ||
654       !tile_cursor.active)
655     return;
656
657   if (tile_cursor.moving)
658   {
659     int step = TILESIZE_VAR / 4;
660     int dx = tile_cursor.target_x - tile_cursor.x;
661     int dy = tile_cursor.target_y - tile_cursor.y;
662
663     if (ABS(dx) < step)
664       tile_cursor.x = tile_cursor.target_x;
665     else
666       tile_cursor.x += SIGN(dx) * step;
667
668     if (ABS(dy) < step)
669       tile_cursor.y = tile_cursor.target_y;
670     else
671       tile_cursor.y += SIGN(dy) * step;
672
673     if (tile_cursor.x == tile_cursor.target_x &&
674         tile_cursor.y == tile_cursor.target_y)
675       tile_cursor.moving = FALSE;
676   }
677
678   dst_x = tile_cursor.x;
679   dst_y = tile_cursor.y;
680
681   frame = getGraphicAnimationFrame(graphic, -1);
682
683   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
684
685   fade_bitmap =
686     (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
687      draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
688
689   if (draw_target == DRAW_TO_SCREEN)
690     BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
691   else
692     BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
693                      dst_x, dst_y);
694 }
695
696 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
697 {
698   int fx = getFieldbufferOffsetX_RND();
699   int fy = getFieldbufferOffsetY_RND();
700
701   BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
702 }
703
704 void BlitScreenToBitmap(Bitmap *target_bitmap)
705 {
706   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
707     BlitScreenToBitmap_EM(target_bitmap);
708   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
709     BlitScreenToBitmap_SP(target_bitmap);
710   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
711     BlitScreenToBitmap_MM(target_bitmap);
712   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
713     BlitScreenToBitmap_RND(target_bitmap);
714
715   redraw_mask |= REDRAW_FIELD;
716 }
717
718 void DrawFramesPerSecond()
719 {
720   char text[100];
721   int font_nr = FONT_TEXT_2;
722   int font_width = getFontWidth(font_nr);
723   int draw_deactivation_mask = GetDrawDeactivationMask();
724   boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
725
726   /* draw FPS with leading space (needed if field buffer deactivated) */
727   sprintf(text, " %04.1f fps", global.frames_per_second);
728
729   /* override draw deactivation mask (required for invisible warp mode) */
730   SetDrawDeactivationMask(REDRAW_NONE);
731
732   /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
733   DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
734               font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
735
736   /* set draw deactivation mask to previous value */
737   SetDrawDeactivationMask(draw_deactivation_mask);
738
739   /* force full-screen redraw in this frame */
740   redraw_mask = REDRAW_ALL;
741 }
742
743 #if DEBUG_FRAME_TIME
744 static void PrintFrameTimeDebugging()
745 {
746   static unsigned int last_counter = 0;
747   unsigned int counter = Counter();
748   int diff_1 = counter - last_counter;
749   int diff_2 = diff_1 - GAME_FRAME_DELAY;
750   int diff_2_max = 20;
751   int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
752   char diff_bar[2 * diff_2_max + 5];
753   int pos = 0;
754   int i;
755
756   diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
757
758   for (i = 0; i < diff_2_max; i++)
759     diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
760                        i >= diff_2_max - diff_2_cut ? '-' : ' ');
761
762   diff_bar[pos++] = '|';
763
764   for (i = 0; i < diff_2_max; i++)
765     diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
766
767   diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
768
769   diff_bar[pos++] = '\0';
770
771   Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
772         counter,
773         diff_1,
774         (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
775         diff_bar);
776
777   last_counter = counter;
778 }
779 #endif
780
781 static int unifiedRedrawMask(int mask)
782 {
783   if (mask & REDRAW_ALL)
784     return REDRAW_ALL;
785
786   if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
787     return REDRAW_ALL;
788
789   return mask;
790 }
791
792 static boolean equalRedrawMasks(int mask_1, int mask_2)
793 {
794   return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
795 }
796
797 void BackToFront()
798 {
799   static int last_redraw_mask = REDRAW_NONE;
800
801   // force screen redraw in every frame to continue drawing global animations
802   // (but always use the last redraw mask to prevent unwanted side effects)
803   if (redraw_mask == REDRAW_NONE)
804     redraw_mask = last_redraw_mask;
805
806   last_redraw_mask = redraw_mask;
807
808 #if 1
809   // masked border now drawn immediately when blitting backbuffer to window
810 #else
811   // draw masked border to all viewports, if defined
812   DrawMaskedBorder(redraw_mask);
813 #endif
814
815   // draw frames per second (only if debug mode is enabled)
816   if (redraw_mask & REDRAW_FPS)
817     DrawFramesPerSecond();
818
819   // remove playfield redraw before potentially merging with doors redraw
820   if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
821     redraw_mask &= ~REDRAW_FIELD;
822
823   // redraw complete window if both playfield and (some) doors need redraw
824   if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
825     redraw_mask = REDRAW_ALL;
826
827   /* although redrawing the whole window would be fine for normal gameplay,
828      being able to only redraw the playfield is required for deactivating
829      certain drawing areas (mainly playfield) to work, which is needed for
830      warp-forward to be fast enough (by skipping redraw of most frames) */
831
832   if (redraw_mask & REDRAW_ALL)
833   {
834     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
835   }
836   else if (redraw_mask & REDRAW_FIELD)
837   {
838     BlitBitmap(backbuffer, window,
839                REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
840   }
841   else if (redraw_mask & REDRAW_DOORS)
842   {
843     // merge door areas to prevent calling screen redraw more than once
844     int x1 = WIN_XSIZE;
845     int y1 = WIN_YSIZE;
846     int x2 = 0;
847     int y2 = 0;
848
849     if (redraw_mask & REDRAW_DOOR_1)
850     {
851       x1 = MIN(x1, DX);
852       y1 = MIN(y1, DY);
853       x2 = MAX(x2, DX + DXSIZE);
854       y2 = MAX(y2, DY + DYSIZE);
855     }
856
857     if (redraw_mask & REDRAW_DOOR_2)
858     {
859       x1 = MIN(x1, VX);
860       y1 = MIN(y1, VY);
861       x2 = MAX(x2, VX + VXSIZE);
862       y2 = MAX(y2, VY + VYSIZE);
863     }
864
865     if (redraw_mask & REDRAW_DOOR_3)
866     {
867       x1 = MIN(x1, EX);
868       y1 = MIN(y1, EY);
869       x2 = MAX(x2, EX + EXSIZE);
870       y2 = MAX(y2, EY + EYSIZE);
871     }
872
873     // make sure that at least one pixel is blitted, and inside the screen
874     // (else nothing is blitted, causing the animations not to be updated)
875     x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
876     y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
877     x2 = MIN(MAX(1, x2), WIN_XSIZE);
878     y2 = MIN(MAX(1, y2), WIN_YSIZE);
879
880     BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
881   }
882
883   redraw_mask = REDRAW_NONE;
884
885 #if DEBUG_FRAME_TIME
886   PrintFrameTimeDebugging();
887 #endif
888 }
889
890 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
891 {
892   unsigned int frame_delay_value_old = GetVideoFrameDelay();
893
894   SetVideoFrameDelay(frame_delay_value);
895
896   BackToFront();
897
898   SetVideoFrameDelay(frame_delay_value_old);
899 }
900
901 static int fade_type_skip = FADE_TYPE_NONE;
902
903 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
904 {
905   void (*draw_border_function)(void) = NULL;
906   int x, y, width, height;
907   int fade_delay, post_delay;
908
909   if (fade_type == FADE_TYPE_FADE_OUT)
910   {
911     if (fade_type_skip != FADE_TYPE_NONE)
912     {
913       /* skip all fade operations until specified fade operation */
914       if (fade_type & fade_type_skip)
915         fade_type_skip = FADE_TYPE_NONE;
916
917       return;
918     }
919
920     if (fading.fade_mode & FADE_TYPE_TRANSFORM)
921       return;
922   }
923
924   redraw_mask |= fade_mask;
925
926   if (fade_type == FADE_TYPE_SKIP)
927   {
928     fade_type_skip = fade_mode;
929
930     return;
931   }
932
933   fade_delay = fading.fade_delay;
934   post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
935
936   if (fade_type_skip != FADE_TYPE_NONE)
937   {
938     /* skip all fade operations until specified fade operation */
939     if (fade_type & fade_type_skip)
940       fade_type_skip = FADE_TYPE_NONE;
941
942     fade_delay = 0;
943   }
944
945   if (global.autoplay_leveldir)
946   {
947     return;
948   }
949
950   if (fade_mask == REDRAW_FIELD)
951   {
952     x = FADE_SX;
953     y = FADE_SY;
954     width  = FADE_SXSIZE;
955     height = FADE_SYSIZE;
956
957     if (border.draw_masked_when_fading)
958       draw_border_function = DrawMaskedBorder_FIELD;    /* update when fading */
959     else
960       DrawMaskedBorder_FIELD();                         /* draw once */
961   }
962   else          /* REDRAW_ALL */
963   {
964     x = 0;
965     y = 0;
966     width  = WIN_XSIZE;
967     height = WIN_YSIZE;
968   }
969
970   if (!setup.fade_screens ||
971       fade_delay == 0 ||
972       fading.fade_mode == FADE_MODE_NONE)
973   {
974     if (fade_mode == FADE_MODE_FADE_OUT)
975       return;
976
977     BlitBitmap(backbuffer, window, x, y, width, height, x, y);
978
979     redraw_mask &= ~fade_mask;
980
981     return;
982   }
983
984   FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
985                 draw_border_function);
986
987   redraw_mask &= ~fade_mask;
988 }
989
990 static void SetScreenStates_BeforeFadingIn()
991 {
992   // temporarily set screen mode for animations to screen after fading in
993   global.anim_status = global.anim_status_next;
994
995   // store backbuffer with all animations that will be started after fading in
996   if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
997     PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
998
999   // set screen mode for animations back to fading
1000   global.anim_status = GAME_MODE_PSEUDO_FADING;
1001 }
1002
1003 static void SetScreenStates_AfterFadingIn()
1004 {
1005   // store new source screen (to use correct masked border for fading)
1006   gfx.fade_border_source_status = global.border_status;
1007
1008   global.anim_status = global.anim_status_next;
1009 }
1010
1011 static void SetScreenStates_BeforeFadingOut()
1012 {
1013   // store new target screen (to use correct masked border for fading)
1014   gfx.fade_border_target_status = game_status;
1015
1016   // set screen mode for animations to fading
1017   global.anim_status = GAME_MODE_PSEUDO_FADING;
1018
1019   // store backbuffer with all animations that will be stopped for fading out
1020   if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1021     PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1022 }
1023
1024 static void SetScreenStates_AfterFadingOut()
1025 {
1026   global.border_status = game_status;
1027 }
1028
1029 void FadeIn(int fade_mask)
1030 {
1031   SetScreenStates_BeforeFadingIn();
1032
1033 #if 1
1034   DrawMaskedBorder(REDRAW_ALL);
1035 #endif
1036
1037   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1038     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1039   else
1040     FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1041
1042   FADE_SX = REAL_SX;
1043   FADE_SY = REAL_SY;
1044   FADE_SXSIZE = FULL_SXSIZE;
1045   FADE_SYSIZE = FULL_SYSIZE;
1046
1047   if (game_status == GAME_MODE_PLAYING &&
1048       strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1049     SetOverlayActive(TRUE);
1050
1051   SetScreenStates_AfterFadingIn();
1052
1053   // force update of global animation status in case of rapid screen changes
1054   redraw_mask = REDRAW_ALL;
1055   BackToFront();
1056 }
1057
1058 void FadeOut(int fade_mask)
1059 {
1060   // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1061   if (!equalRedrawMasks(fade_mask, redraw_mask))
1062     BackToFront();
1063
1064   SetScreenStates_BeforeFadingOut();
1065
1066   SetTileCursorActive(FALSE);
1067   SetOverlayActive(FALSE);
1068
1069 #if 0
1070   DrawMaskedBorder(REDRAW_ALL);
1071 #endif
1072
1073   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1074     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1075   else
1076     FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1077
1078   SetScreenStates_AfterFadingOut();
1079 }
1080
1081 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1082 {
1083   static struct TitleFadingInfo fading_leave_stored;
1084
1085   if (set)
1086     fading_leave_stored = fading_leave;
1087   else
1088     fading = fading_leave_stored;
1089 }
1090
1091 void FadeSetEnterMenu()
1092 {
1093   fading = menu.enter_menu;
1094
1095   FadeSetLeaveNext(fading, TRUE);       /* (keep same fade mode) */
1096 }
1097
1098 void FadeSetLeaveMenu()
1099 {
1100   fading = menu.leave_menu;
1101
1102   FadeSetLeaveNext(fading, TRUE);       /* (keep same fade mode) */
1103 }
1104
1105 void FadeSetEnterScreen()
1106 {
1107   fading = menu.enter_screen[game_status];
1108
1109   FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);       /* store */
1110 }
1111
1112 void FadeSetNextScreen()
1113 {
1114   fading = menu.next_screen[game_status];
1115
1116   // (do not overwrite fade mode set by FadeSetEnterScreen)
1117   // FadeSetLeaveNext(fading, TRUE);    /* (keep same fade mode) */
1118 }
1119
1120 void FadeSetLeaveScreen()
1121 {
1122   FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);      /* recall */
1123 }
1124
1125 void FadeSetFromType(int type)
1126 {
1127   if (type & TYPE_ENTER_SCREEN)
1128     FadeSetEnterScreen();
1129   else if (type & TYPE_ENTER)
1130     FadeSetEnterMenu();
1131   else if (type & TYPE_LEAVE)
1132     FadeSetLeaveMenu();
1133 }
1134
1135 void FadeSetDisabled()
1136 {
1137   static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1138
1139   fading = fading_none;
1140 }
1141
1142 void FadeSkipNextFadeIn()
1143 {
1144   FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1145 }
1146
1147 void FadeSkipNextFadeOut()
1148 {
1149   FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1150 }
1151
1152 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1153 {
1154   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1155
1156   return (graphic == IMG_UNDEFINED ? NULL :
1157           graphic_info[graphic].bitmap != NULL || redefined ?
1158           graphic_info[graphic].bitmap :
1159           graphic_info[default_graphic].bitmap);
1160 }
1161
1162 Bitmap *getBackgroundBitmap(int graphic)
1163 {
1164   return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1165 }
1166
1167 Bitmap *getGlobalBorderBitmap(int graphic)
1168 {
1169   return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1170 }
1171
1172 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1173 {
1174   int graphic =
1175     (status == GAME_MODE_MAIN ||
1176      status == GAME_MODE_PSEUDO_TYPENAME        ? IMG_GLOBAL_BORDER_MAIN :
1177      status == GAME_MODE_SCORES                 ? IMG_GLOBAL_BORDER_SCORES :
1178      status == GAME_MODE_EDITOR                 ? IMG_GLOBAL_BORDER_EDITOR :
1179      status == GAME_MODE_PLAYING                ? IMG_GLOBAL_BORDER_PLAYING :
1180      IMG_GLOBAL_BORDER);
1181
1182   return getGlobalBorderBitmap(graphic);
1183 }
1184
1185 void SetWindowBackgroundImageIfDefined(int graphic)
1186 {
1187   if (graphic_info[graphic].bitmap)
1188     SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1189 }
1190
1191 void SetMainBackgroundImageIfDefined(int graphic)
1192 {
1193   if (graphic_info[graphic].bitmap)
1194     SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1195 }
1196
1197 void SetDoorBackgroundImageIfDefined(int graphic)
1198 {
1199   if (graphic_info[graphic].bitmap)
1200     SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1201 }
1202
1203 void SetWindowBackgroundImage(int graphic)
1204 {
1205   SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1206 }
1207
1208 void SetMainBackgroundImage(int graphic)
1209 {
1210   SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1211 }
1212
1213 void SetDoorBackgroundImage(int graphic)
1214 {
1215   SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1216 }
1217
1218 void SetPanelBackground()
1219 {
1220   struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1221
1222   BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1223                   gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1224
1225   SetDoorBackgroundBitmap(bitmap_db_panel);
1226 }
1227
1228 void DrawBackground(int x, int y, int width, int height)
1229 {
1230   /* "drawto" might still point to playfield buffer here (hall of fame) */
1231   ClearRectangleOnBackground(backbuffer, x, y, width, height);
1232
1233   if (IN_GFX_FIELD_FULL(x, y))
1234     redraw_mask |= REDRAW_FIELD;
1235   else if (IN_GFX_DOOR_1(x, y))
1236     redraw_mask |= REDRAW_DOOR_1;
1237   else if (IN_GFX_DOOR_2(x, y))
1238     redraw_mask |= REDRAW_DOOR_2;
1239   else if (IN_GFX_DOOR_3(x, y))
1240     redraw_mask |= REDRAW_DOOR_3;
1241 }
1242
1243 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1244 {
1245   struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1246
1247   if (font->bitmap == NULL)
1248     return;
1249
1250   DrawBackground(x, y, width, height);
1251 }
1252
1253 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1254 {
1255   struct GraphicInfo *g = &graphic_info[graphic];
1256
1257   if (g->bitmap == NULL)
1258     return;
1259
1260   DrawBackground(x, y, width, height);
1261 }
1262
1263 static int game_status_last = -1;
1264 static Bitmap *global_border_bitmap_last = NULL;
1265 static Bitmap *global_border_bitmap = NULL;
1266 static int real_sx_last = -1, real_sy_last = -1;
1267 static int full_sxsize_last = -1, full_sysize_last = -1;
1268 static int dx_last = -1, dy_last = -1;
1269 static int dxsize_last = -1, dysize_last = -1;
1270 static int vx_last = -1, vy_last = -1;
1271 static int vxsize_last = -1, vysize_last = -1;
1272
1273 boolean CheckIfGlobalBorderHasChanged()
1274 {
1275   // if game status has not changed, global border has not changed either
1276   if (game_status == game_status_last)
1277     return FALSE;
1278
1279   // determine and store new global border bitmap for current game status
1280   global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1281
1282   return (global_border_bitmap_last != global_border_bitmap);
1283 }
1284
1285 boolean CheckIfGlobalBorderRedrawIsNeeded()
1286 {
1287   // if game status has not changed, nothing has to be redrawn
1288   if (game_status == game_status_last)
1289     return FALSE;
1290
1291   // redraw if last screen was title screen
1292   if (game_status_last == GAME_MODE_TITLE)
1293     return TRUE;
1294
1295   // redraw if global screen border has changed
1296   if (CheckIfGlobalBorderHasChanged())
1297     return TRUE;
1298
1299   // redraw if position or size of playfield area has changed
1300   if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1301       full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1302     return TRUE;
1303
1304   // redraw if position or size of door area has changed
1305   if (dx_last != DX || dy_last != DY ||
1306       dxsize_last != DXSIZE || dysize_last != DYSIZE)
1307     return TRUE;
1308
1309   // redraw if position or size of tape area has changed
1310   if (vx_last != VX || vy_last != VY ||
1311       vxsize_last != VXSIZE || vysize_last != VYSIZE)
1312     return TRUE;
1313
1314   return FALSE;
1315 }
1316
1317 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1318 {
1319   if (bitmap)
1320     BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1321   else
1322     ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1323 }
1324
1325 void RedrawGlobalBorder()
1326 {
1327   Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1328
1329   RedrawGlobalBorderFromBitmap(bitmap);
1330
1331   redraw_mask = REDRAW_ALL;
1332 }
1333
1334 static void RedrawGlobalBorderIfNeeded()
1335 {
1336   if (game_status == game_status_last)
1337     return;
1338
1339   // copy current draw buffer to later copy back areas that have not changed
1340   if (game_status_last != GAME_MODE_TITLE)
1341     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1342
1343   if (CheckIfGlobalBorderRedrawIsNeeded())
1344   {
1345     // redraw global screen border (or clear, if defined to be empty)
1346     RedrawGlobalBorderFromBitmap(global_border_bitmap);
1347
1348     // copy previous playfield and door areas, if they are defined on both
1349     // previous and current screen and if they still have the same size
1350
1351     if (real_sx_last != -1 && real_sy_last != -1 &&
1352         REAL_SX != -1 && REAL_SY != -1 &&
1353         full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1354       BlitBitmap(bitmap_db_store_1, backbuffer,
1355                  real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1356                  REAL_SX, REAL_SY);
1357
1358     if (dx_last != -1 && dy_last != -1 &&
1359         DX != -1 && DY != -1 &&
1360         dxsize_last == DXSIZE && dysize_last == DYSIZE)
1361       BlitBitmap(bitmap_db_store_1, backbuffer,
1362                  dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1363
1364     if (vx_last != -1 && vy_last != -1 &&
1365         VX != -1 && VY != -1 &&
1366         vxsize_last == VXSIZE && vysize_last == VYSIZE)
1367       BlitBitmap(bitmap_db_store_1, backbuffer,
1368                  vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1369
1370     redraw_mask = REDRAW_ALL;
1371   }
1372
1373   game_status_last = game_status;
1374
1375   global_border_bitmap_last = global_border_bitmap;
1376
1377   real_sx_last = REAL_SX;
1378   real_sy_last = REAL_SY;
1379   full_sxsize_last = FULL_SXSIZE;
1380   full_sysize_last = FULL_SYSIZE;
1381   dx_last = DX;
1382   dy_last = DY;
1383   dxsize_last = DXSIZE;
1384   dysize_last = DYSIZE;
1385   vx_last = VX;
1386   vy_last = VY;
1387   vxsize_last = VXSIZE;
1388   vysize_last = VYSIZE;
1389 }
1390
1391 void ClearField()
1392 {
1393   RedrawGlobalBorderIfNeeded();
1394
1395   /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1396   /* (when entering hall of fame after playing) */
1397   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1398
1399   /* !!! maybe this should be done before clearing the background !!! */
1400   if (game_status == GAME_MODE_PLAYING)
1401   {
1402     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1403     SetDrawtoField(DRAW_TO_FIELDBUFFER);
1404   }
1405   else
1406   {
1407     SetDrawtoField(DRAW_TO_BACKBUFFER);
1408   }
1409 }
1410
1411 void MarkTileDirty(int x, int y)
1412 {
1413   redraw_mask |= REDRAW_FIELD;
1414 }
1415
1416 void SetBorderElement()
1417 {
1418   int x, y;
1419
1420   BorderElement = EL_EMPTY;
1421
1422   /* the MM game engine does not use a visible border element */
1423   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1424     return;
1425
1426   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1427   {
1428     for (x = 0; x < lev_fieldx; x++)
1429     {
1430       if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1431         BorderElement = EL_STEELWALL;
1432
1433       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1434         x = lev_fieldx - 2;
1435     }
1436   }
1437 }
1438
1439 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1440                        int max_array_fieldx, int max_array_fieldy,
1441                        short field[max_array_fieldx][max_array_fieldy],
1442                        int max_fieldx, int max_fieldy)
1443 {
1444   int i,x,y;
1445   int old_element;
1446   static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1447   static int safety = 0;
1448
1449   /* check if starting field still has the desired content */
1450   if (field[from_x][from_y] == fill_element)
1451     return;
1452
1453   safety++;
1454
1455   if (safety > max_fieldx * max_fieldy)
1456     Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1457
1458   old_element = field[from_x][from_y];
1459   field[from_x][from_y] = fill_element;
1460
1461   for (i = 0; i < 4; i++)
1462   {
1463     x = from_x + check[i][0];
1464     y = from_y + check[i][1];
1465
1466     if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1467       FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1468                         field, max_fieldx, max_fieldy);
1469   }
1470
1471   safety--;
1472 }
1473
1474 void FloodFillLevel(int from_x, int from_y, int fill_element,
1475                     short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1476                     int max_fieldx, int max_fieldy)
1477 {
1478   FloodFillLevelExt(from_x, from_y, fill_element,
1479                     MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1480                     max_fieldx, max_fieldy);
1481 }
1482
1483 void SetRandomAnimationValue(int x, int y)
1484 {
1485   gfx.anim_random_frame = GfxRandom[x][y];
1486 }
1487
1488 int getGraphicAnimationFrame(int graphic, int sync_frame)
1489 {
1490   /* animation synchronized with global frame counter, not move position */
1491   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1492     sync_frame = FrameCounter;
1493
1494   return getAnimationFrame(graphic_info[graphic].anim_frames,
1495                            graphic_info[graphic].anim_delay,
1496                            graphic_info[graphic].anim_mode,
1497                            graphic_info[graphic].anim_start_frame,
1498                            sync_frame);
1499 }
1500
1501 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1502 {
1503   struct GraphicInfo *g = &graphic_info[graphic];
1504   int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1505
1506   if (tilesize == gfx.standard_tile_size)
1507     *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1508   else if (tilesize == game.tile_size)
1509     *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1510   else
1511     *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1512 }
1513
1514 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1515                         boolean get_backside)
1516 {
1517   struct GraphicInfo *g = &graphic_info[graphic];
1518   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1519   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1520
1521   if (g->offset_y == 0)         /* frames are ordered horizontally */
1522   {
1523     int max_width = g->anim_frames_per_line * g->width;
1524     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1525
1526     *x = pos % max_width;
1527     *y = src_y % g->height + pos / max_width * g->height;
1528   }
1529   else if (g->offset_x == 0)    /* frames are ordered vertically */
1530   {
1531     int max_height = g->anim_frames_per_line * g->height;
1532     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1533
1534     *x = src_x % g->width + pos / max_height * g->width;
1535     *y = pos % max_height;
1536   }
1537   else                          /* frames are ordered diagonally */
1538   {
1539     *x = src_x + frame * g->offset_x;
1540     *y = src_y + frame * g->offset_y;
1541   }
1542 }
1543
1544 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1545                               Bitmap **bitmap, int *x, int *y,
1546                               boolean get_backside)
1547 {
1548   struct GraphicInfo *g = &graphic_info[graphic];
1549
1550   // if no in-game graphics defined, always use standard graphic size
1551   if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1552     tilesize = TILESIZE;
1553
1554   getGraphicSourceBitmap(graphic, tilesize, bitmap);
1555   getGraphicSourceXY(graphic, frame, x, y, get_backside);
1556
1557   *x = *x * tilesize / g->tile_size;
1558   *y = *y * tilesize / g->tile_size;
1559 }
1560
1561 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1562                            Bitmap **bitmap, int *x, int *y)
1563 {
1564   getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1565 }
1566
1567 void getFixedGraphicSource(int graphic, int frame,
1568                            Bitmap **bitmap, int *x, int *y)
1569 {
1570   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1571 }
1572
1573 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1574 {
1575   getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1576 }
1577
1578 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1579                                        int *x, int *y, boolean get_backside)
1580 {
1581   getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1582                            get_backside);
1583 }
1584
1585 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1586 {
1587   getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1588 }
1589
1590 void DrawGraphic(int x, int y, int graphic, int frame)
1591 {
1592 #if DEBUG
1593   if (!IN_SCR_FIELD(x, y))
1594   {
1595     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1596     printf("DrawGraphic(): This should never happen!\n");
1597     return;
1598   }
1599 #endif
1600
1601   DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1602                  frame);
1603
1604   MarkTileDirty(x, y);
1605 }
1606
1607 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1608 {
1609 #if DEBUG
1610   if (!IN_SCR_FIELD(x, y))
1611   {
1612     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1613     printf("DrawGraphic(): This should never happen!\n");
1614     return;
1615   }
1616 #endif
1617
1618   DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1619                       frame);
1620   MarkTileDirty(x, y);
1621 }
1622
1623 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1624                     int frame)
1625 {
1626   Bitmap *src_bitmap;
1627   int src_x, src_y;
1628
1629   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1630
1631   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1632 }
1633
1634 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1635                          int frame)
1636 {
1637   Bitmap *src_bitmap;
1638   int src_x, src_y;
1639
1640   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1641   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1642 }
1643
1644 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1645 {
1646 #if DEBUG
1647   if (!IN_SCR_FIELD(x, y))
1648   {
1649     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1650     printf("DrawGraphicThruMask(): This should never happen!\n");
1651     return;
1652   }
1653 #endif
1654
1655   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1656                          graphic, frame);
1657
1658   MarkTileDirty(x, y);
1659 }
1660
1661 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1662 {
1663 #if DEBUG
1664   if (!IN_SCR_FIELD(x, y))
1665   {
1666     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1667     printf("DrawGraphicThruMask(): This should never happen!\n");
1668     return;
1669   }
1670 #endif
1671
1672   DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1673                               graphic, frame);
1674   MarkTileDirty(x, y);
1675 }
1676
1677 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1678                             int frame)
1679 {
1680   Bitmap *src_bitmap;
1681   int src_x, src_y;
1682
1683   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1684
1685   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1686                    dst_x, dst_y);
1687 }
1688
1689 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1690                                  int graphic, int frame)
1691 {
1692   Bitmap *src_bitmap;
1693   int src_x, src_y;
1694
1695   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1696
1697   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1698                    dst_x, dst_y);
1699 }
1700
1701 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1702 {
1703   DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1704                       frame, tilesize);
1705   MarkTileDirty(x / tilesize, y / tilesize);
1706 }
1707
1708 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1709                               int tilesize)
1710 {
1711   DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1712                               graphic, frame, tilesize);
1713   MarkTileDirty(x / tilesize, y / tilesize);
1714 }
1715
1716 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1717                          int tilesize)
1718 {
1719   Bitmap *src_bitmap;
1720   int src_x, src_y;
1721
1722   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1723   BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1724 }
1725
1726 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1727                                  int frame, int tilesize)
1728 {
1729   Bitmap *src_bitmap;
1730   int src_x, src_y;
1731
1732   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1733   BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1734 }
1735
1736 void DrawMiniGraphic(int x, int y, int graphic)
1737 {
1738   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1739   MarkTileDirty(x / 2, y / 2);
1740 }
1741
1742 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1743 {
1744   Bitmap *src_bitmap;
1745   int src_x, src_y;
1746
1747   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1748   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1749 }
1750
1751 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1752                                             int graphic, int frame,
1753                                             int cut_mode, int mask_mode)
1754 {
1755   Bitmap *src_bitmap;
1756   int src_x, src_y;
1757   int dst_x, dst_y;
1758   int width = TILEX, height = TILEY;
1759   int cx = 0, cy = 0;
1760
1761   if (dx || dy)                 /* shifted graphic */
1762   {
1763     if (x < BX1)                /* object enters playfield from the left */
1764     {
1765       x = BX1;
1766       width = dx;
1767       cx = TILEX - dx;
1768       dx = 0;
1769     }
1770     else if (x > BX2)           /* object enters playfield from the right */
1771     {
1772       x = BX2;
1773       width = -dx;
1774       dx = TILEX + dx;
1775     }
1776     else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1777     {
1778       width += dx;
1779       cx = -dx;
1780       dx = 0;
1781     }
1782     else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1783       width -= dx;
1784     else if (dx)                /* general horizontal movement */
1785       MarkTileDirty(x + SIGN(dx), y);
1786
1787     if (y < BY1)                /* object enters playfield from the top */
1788     {
1789       if (cut_mode == CUT_BELOW) /* object completely above top border */
1790         return;
1791
1792       y = BY1;
1793       height = dy;
1794       cy = TILEY - dy;
1795       dy = 0;
1796     }
1797     else if (y > BY2)           /* object enters playfield from the bottom */
1798     {
1799       y = BY2;
1800       height = -dy;
1801       dy = TILEY + dy;
1802     }
1803     else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1804     {
1805       height += dy;
1806       cy = -dy;
1807       dy = 0;
1808     }
1809     else if (dy > 0 && cut_mode == CUT_ABOVE)
1810     {
1811       if (y == BY2)             /* object completely above bottom border */
1812         return;
1813
1814       height = dy;
1815       cy = TILEY - dy;
1816       dy = TILEY;
1817       MarkTileDirty(x, y + 1);
1818     }                           /* object leaves playfield to the bottom */
1819     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1820       height -= dy;
1821     else if (dy)                /* general vertical movement */
1822       MarkTileDirty(x, y + SIGN(dy));
1823   }
1824
1825 #if DEBUG
1826   if (!IN_SCR_FIELD(x, y))
1827   {
1828     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1829     printf("DrawGraphicShifted(): This should never happen!\n");
1830     return;
1831   }
1832 #endif
1833
1834   width = width * TILESIZE_VAR / TILESIZE;
1835   height = height * TILESIZE_VAR / TILESIZE;
1836   cx = cx * TILESIZE_VAR / TILESIZE;
1837   cy = cy * TILESIZE_VAR / TILESIZE;
1838   dx = dx * TILESIZE_VAR / TILESIZE;
1839   dy = dy * TILESIZE_VAR / TILESIZE;
1840
1841   if (width > 0 && height > 0)
1842   {
1843     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1844
1845     src_x += cx;
1846     src_y += cy;
1847
1848     dst_x = FX + x * TILEX_VAR + dx;
1849     dst_y = FY + y * TILEY_VAR + dy;
1850
1851     if (mask_mode == USE_MASKING)
1852       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1853                        dst_x, dst_y);
1854     else
1855       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1856                  dst_x, dst_y);
1857
1858     MarkTileDirty(x, y);
1859   }
1860 }
1861
1862 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1863                                             int graphic, int frame,
1864                                             int cut_mode, int mask_mode)
1865 {
1866   Bitmap *src_bitmap;
1867   int src_x, src_y;
1868   int dst_x, dst_y;
1869   int width = TILEX_VAR, height = TILEY_VAR;
1870   int x1 = x;
1871   int y1 = y;
1872   int x2 = x + SIGN(dx);
1873   int y2 = y + SIGN(dy);
1874
1875   /* movement with two-tile animations must be sync'ed with movement position,
1876      not with current GfxFrame (which can be higher when using slow movement) */
1877   int anim_pos = (dx ? ABS(dx) : ABS(dy));
1878   int anim_frames = graphic_info[graphic].anim_frames;
1879
1880   /* (we also need anim_delay here for movement animations with less frames) */
1881   int anim_delay = graphic_info[graphic].anim_delay;
1882   int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1883
1884   boolean draw_start_tile = (cut_mode != CUT_ABOVE);    /* only for falling! */
1885   boolean draw_end_tile   = (cut_mode != CUT_BELOW);    /* only for falling! */
1886
1887   /* re-calculate animation frame for two-tile movement animation */
1888   frame = getGraphicAnimationFrame(graphic, sync_frame);
1889
1890   /* check if movement start graphic inside screen area and should be drawn */
1891   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1892   {
1893     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1894
1895     dst_x = FX + x1 * TILEX_VAR;
1896     dst_y = FY + y1 * TILEY_VAR;
1897
1898     if (mask_mode == USE_MASKING)
1899       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1900                        dst_x, dst_y);
1901     else
1902       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1903                  dst_x, dst_y);
1904
1905     MarkTileDirty(x1, y1);
1906   }
1907
1908   /* check if movement end graphic inside screen area and should be drawn */
1909   if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1910   {
1911     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1912
1913     dst_x = FX + x2 * TILEX_VAR;
1914     dst_y = FY + y2 * TILEY_VAR;
1915
1916     if (mask_mode == USE_MASKING)
1917       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1918                        dst_x, dst_y);
1919     else
1920       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1921                  dst_x, dst_y);
1922
1923     MarkTileDirty(x2, y2);
1924   }
1925 }
1926
1927 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1928                                int graphic, int frame,
1929                                int cut_mode, int mask_mode)
1930 {
1931   if (graphic < 0)
1932   {
1933     DrawGraphic(x, y, graphic, frame);
1934
1935     return;
1936   }
1937
1938   if (graphic_info[graphic].double_movement)    /* EM style movement images */
1939     DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1940   else
1941     DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1942 }
1943
1944 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1945                                 int frame, int cut_mode)
1946 {
1947   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1948 }
1949
1950 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1951                           int cut_mode, int mask_mode)
1952 {
1953   int lx = LEVELX(x), ly = LEVELY(y);
1954   int graphic;
1955   int frame;
1956
1957   if (IN_LEV_FIELD(lx, ly))
1958   {
1959     SetRandomAnimationValue(lx, ly);
1960
1961     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1962     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1963
1964     /* do not use double (EM style) movement graphic when not moving */
1965     if (graphic_info[graphic].double_movement && !dx && !dy)
1966     {
1967       graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1968       frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1969     }
1970   }
1971   else  /* border element */
1972   {
1973     graphic = el2img(element);
1974     frame = getGraphicAnimationFrame(graphic, -1);
1975   }
1976
1977   if (element == EL_EXPANDABLE_WALL)
1978   {
1979     boolean left_stopped = FALSE, right_stopped = FALSE;
1980
1981     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1982       left_stopped = TRUE;
1983     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1984       right_stopped = TRUE;
1985
1986     if (left_stopped && right_stopped)
1987       graphic = IMG_WALL;
1988     else if (left_stopped)
1989     {
1990       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1991       frame = graphic_info[graphic].anim_frames - 1;
1992     }
1993     else if (right_stopped)
1994     {
1995       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1996       frame = graphic_info[graphic].anim_frames - 1;
1997     }
1998   }
1999
2000   if (dx || dy)
2001     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2002   else if (mask_mode == USE_MASKING)
2003     DrawGraphicThruMask(x, y, graphic, frame);
2004   else
2005     DrawGraphic(x, y, graphic, frame);
2006 }
2007
2008 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2009                          int cut_mode, int mask_mode)
2010 {
2011   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2012     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2013                          cut_mode, mask_mode);
2014 }
2015
2016 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2017                               int cut_mode)
2018 {
2019   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2020 }
2021
2022 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2023                              int cut_mode)
2024 {
2025   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2026 }
2027
2028 void DrawLevelElementThruMask(int x, int y, int element)
2029 {
2030   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2031 }
2032
2033 void DrawLevelFieldThruMask(int x, int y)
2034 {
2035   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2036 }
2037
2038 /* !!! implementation of quicksand is totally broken !!! */
2039 #define IS_CRUMBLED_TILE(x, y, e)                                       \
2040         (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
2041                              !IS_MOVING(x, y) ||                        \
2042                              (e) == EL_QUICKSAND_EMPTYING ||            \
2043                              (e) == EL_QUICKSAND_FAST_EMPTYING))
2044
2045 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2046                                                int graphic)
2047 {
2048   Bitmap *src_bitmap;
2049   int src_x, src_y;
2050   int width, height, cx, cy;
2051   int sx = SCREENX(x), sy = SCREENY(y);
2052   int crumbled_border_size = graphic_info[graphic].border_size;
2053   int crumbled_tile_size = graphic_info[graphic].tile_size;
2054   int crumbled_border_size_var =
2055     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2056   int i;
2057
2058   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2059
2060   for (i = 1; i < 4; i++)
2061   {
2062     int dxx = (i & 1 ? dx : 0);
2063     int dyy = (i & 2 ? dy : 0);
2064     int xx = x + dxx;
2065     int yy = y + dyy;
2066     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2067                    BorderElement);
2068
2069     /* check if neighbour field is of same crumble type */
2070     boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2071                     graphic_info[graphic].class ==
2072                     graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2073
2074     /* return if check prevents inner corner */
2075     if (same == (dxx == dx && dyy == dy))
2076       return;
2077   }
2078
2079   /* if we reach this point, we have an inner corner */
2080
2081   getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2082
2083   width  = crumbled_border_size_var;
2084   height = crumbled_border_size_var;
2085   cx = (dx > 0 ? TILESIZE_VAR - width  : 0);
2086   cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2087
2088   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2089              width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2090 }
2091
2092 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2093                                           int dir)
2094 {
2095   Bitmap *src_bitmap;
2096   int src_x, src_y;
2097   int width, height, bx, by, cx, cy;
2098   int sx = SCREENX(x), sy = SCREENY(y);
2099   int crumbled_border_size = graphic_info[graphic].border_size;
2100   int crumbled_tile_size = graphic_info[graphic].tile_size;
2101   int crumbled_border_size_var =
2102     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2103   int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2104   int i;
2105
2106   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2107
2108   /* draw simple, sloppy, non-corner-accurate crumbled border */
2109
2110   width  = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2111   height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2112   cx = (dir == 2 ? crumbled_border_pos_var : 0);
2113   cy = (dir == 3 ? crumbled_border_pos_var : 0);
2114
2115   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2116              FX + sx * TILEX_VAR + cx,
2117              FY + sy * TILEY_VAR + cy);
2118
2119   /* (remaining middle border part must be at least as big as corner part) */
2120   if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2121       crumbled_border_size_var >= TILESIZE_VAR / 3)
2122     return;
2123
2124   /* correct corners of crumbled border, if needed */
2125
2126   for (i = -1; i <= 1; i += 2)
2127   {
2128     int xx = x + (dir == 0 || dir == 3 ? i : 0);
2129     int yy = y + (dir == 1 || dir == 2 ? i : 0);
2130     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2131                    BorderElement);
2132
2133     /* check if neighbour field is of same crumble type */
2134     if (IS_CRUMBLED_TILE(xx, yy, element) &&
2135         graphic_info[graphic].class ==
2136         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2137     {
2138       /* no crumbled corner, but continued crumbled border */
2139
2140       int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2141       int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2142       int b1 = (i == 1 ? crumbled_border_size_var :
2143                 TILESIZE_VAR - 2 * crumbled_border_size_var);
2144
2145       width  = crumbled_border_size_var;
2146       height = crumbled_border_size_var;
2147
2148       if (dir == 1 || dir == 2)
2149       {
2150         cx = c1;
2151         cy = c2;
2152         bx = cx;
2153         by = b1;
2154       }
2155       else
2156       {
2157         cx = c2;
2158         cy = c1;
2159         bx = b1;
2160         by = cy;
2161       }
2162
2163       BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2164                  width, height,
2165                  FX + sx * TILEX_VAR + cx,
2166                  FY + sy * TILEY_VAR + cy);
2167     }
2168   }
2169 }
2170
2171 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2172 {
2173   int sx = SCREENX(x), sy = SCREENY(y);
2174   int element;
2175   int i;
2176   static int xy[4][2] =
2177   {
2178     { 0, -1 },
2179     { -1, 0 },
2180     { +1, 0 },
2181     { 0, +1 }
2182   };
2183
2184   if (!IN_LEV_FIELD(x, y))
2185     return;
2186
2187   element = TILE_GFX_ELEMENT(x, y);
2188
2189   if (IS_CRUMBLED_TILE(x, y, element))          /* crumble field itself */
2190   {
2191     if (!IN_SCR_FIELD(sx, sy))
2192       return;
2193
2194     /* crumble field borders towards direct neighbour fields */
2195     for (i = 0; i < 4; i++)
2196     {
2197       int xx = x + xy[i][0];
2198       int yy = y + xy[i][1];
2199
2200       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2201                  BorderElement);
2202
2203       /* check if neighbour field is of same crumble type */
2204       if (IS_CRUMBLED_TILE(xx, yy, element) &&
2205           graphic_info[graphic].class ==
2206           graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2207         continue;
2208
2209       DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2210     }
2211
2212     /* crumble inner field corners towards corner neighbour fields */
2213     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2214         graphic_info[graphic].anim_frames == 2)
2215     {
2216       for (i = 0; i < 4; i++)
2217       {
2218         int dx = (i & 1 ? +1 : -1);
2219         int dy = (i & 2 ? +1 : -1);
2220
2221         DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2222       }
2223     }
2224
2225     MarkTileDirty(sx, sy);
2226   }
2227   else          /* center field is not crumbled -- crumble neighbour fields */
2228   {
2229     /* crumble field borders of direct neighbour fields */
2230     for (i = 0; i < 4; i++)
2231     {
2232       int xx = x + xy[i][0];
2233       int yy = y + xy[i][1];
2234       int sxx = sx + xy[i][0];
2235       int syy = sy + xy[i][1];
2236
2237       if (!IN_LEV_FIELD(xx, yy) ||
2238           !IN_SCR_FIELD(sxx, syy))
2239         continue;
2240
2241       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2242         continue;
2243
2244       element = TILE_GFX_ELEMENT(xx, yy);
2245
2246       if (!IS_CRUMBLED_TILE(xx, yy, element))
2247         continue;
2248
2249       graphic = el_act2crm(element, ACTION_DEFAULT);
2250
2251       DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2252
2253       MarkTileDirty(sxx, syy);
2254     }
2255
2256     /* crumble inner field corners of corner neighbour fields */
2257     for (i = 0; i < 4; i++)
2258     {
2259       int dx = (i & 1 ? +1 : -1);
2260       int dy = (i & 2 ? +1 : -1);
2261       int xx = x + dx;
2262       int yy = y + dy;
2263       int sxx = sx + dx;
2264       int syy = sy + dy;
2265
2266       if (!IN_LEV_FIELD(xx, yy) ||
2267           !IN_SCR_FIELD(sxx, syy))
2268         continue;
2269
2270       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2271         continue;
2272
2273       element = TILE_GFX_ELEMENT(xx, yy);
2274
2275       if (!IS_CRUMBLED_TILE(xx, yy, element))
2276         continue;
2277
2278       graphic = el_act2crm(element, ACTION_DEFAULT);
2279
2280       if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2281           graphic_info[graphic].anim_frames == 2)
2282         DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2283
2284       MarkTileDirty(sxx, syy);
2285     }
2286   }
2287 }
2288
2289 void DrawLevelFieldCrumbled(int x, int y)
2290 {
2291   int graphic;
2292
2293   if (!IN_LEV_FIELD(x, y))
2294     return;
2295
2296   if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2297       GfxElement[x][y] != EL_UNDEFINED &&
2298       GFX_CRUMBLED(GfxElement[x][y]))
2299   {
2300     DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2301
2302     return;
2303   }
2304
2305   graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2306
2307   DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2308 }
2309
2310 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2311                                    int step_frame)
2312 {
2313   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2314   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2315   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2316   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2317   int sx = SCREENX(x), sy = SCREENY(y);
2318
2319   DrawGraphic(sx, sy, graphic1, frame1);
2320   DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2321 }
2322
2323 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2324 {
2325   int sx = SCREENX(x), sy = SCREENY(y);
2326   static int xy[4][2] =
2327   {
2328     { 0, -1 },
2329     { -1, 0 },
2330     { +1, 0 },
2331     { 0, +1 }
2332   };
2333   int i;
2334
2335   /* crumble direct neighbour fields (required for field borders) */
2336   for (i = 0; i < 4; i++)
2337   {
2338     int xx = x + xy[i][0];
2339     int yy = y + xy[i][1];
2340     int sxx = sx + xy[i][0];
2341     int syy = sy + xy[i][1];
2342
2343     if (!IN_LEV_FIELD(xx, yy) ||
2344         !IN_SCR_FIELD(sxx, syy) ||
2345         !GFX_CRUMBLED(Feld[xx][yy]) ||
2346         IS_MOVING(xx, yy))
2347       continue;
2348
2349     DrawLevelField(xx, yy);
2350   }
2351
2352   /* crumble corner neighbour fields (required for inner field corners) */
2353   for (i = 0; i < 4; i++)
2354   {
2355     int dx = (i & 1 ? +1 : -1);
2356     int dy = (i & 2 ? +1 : -1);
2357     int xx = x + dx;
2358     int yy = y + dy;
2359     int sxx = sx + dx;
2360     int syy = sy + dy;
2361
2362     if (!IN_LEV_FIELD(xx, yy) ||
2363         !IN_SCR_FIELD(sxx, syy) ||
2364         !GFX_CRUMBLED(Feld[xx][yy]) ||
2365         IS_MOVING(xx, yy))
2366       continue;
2367
2368     int element = TILE_GFX_ELEMENT(xx, yy);
2369     int graphic = el_act2crm(element, ACTION_DEFAULT);
2370
2371     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2372         graphic_info[graphic].anim_frames == 2)
2373       DrawLevelField(xx, yy);
2374   }
2375 }
2376
2377 static int getBorderElement(int x, int y)
2378 {
2379   int border[7][2] =
2380   {
2381     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
2382     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
2383     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
2384     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2385     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
2386     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
2387     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
2388   };
2389   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2390   int steel_position = (x == -1         && y == -1              ? 0 :
2391                         x == lev_fieldx && y == -1              ? 1 :
2392                         x == -1         && y == lev_fieldy      ? 2 :
2393                         x == lev_fieldx && y == lev_fieldy      ? 3 :
2394                         x == -1         || x == lev_fieldx      ? 4 :
2395                         y == -1         || y == lev_fieldy      ? 5 : 6);
2396
2397   return border[steel_position][steel_type];
2398 }
2399
2400 void DrawScreenElement(int x, int y, int element)
2401 {
2402   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2403   DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2404 }
2405
2406 void DrawLevelElement(int x, int y, int element)
2407 {
2408   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2409     DrawScreenElement(SCREENX(x), SCREENY(y), element);
2410 }
2411
2412 void DrawScreenField(int x, int y)
2413 {
2414   int lx = LEVELX(x), ly = LEVELY(y);
2415   int element, content;
2416
2417   if (!IN_LEV_FIELD(lx, ly))
2418   {
2419     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2420       element = EL_EMPTY;
2421     else
2422       element = getBorderElement(lx, ly);
2423
2424     DrawScreenElement(x, y, element);
2425
2426     return;
2427   }
2428
2429   element = Feld[lx][ly];
2430   content = Store[lx][ly];
2431
2432   if (IS_MOVING(lx, ly))
2433   {
2434     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2435     boolean cut_mode = NO_CUTTING;
2436
2437     if (element == EL_QUICKSAND_EMPTYING ||
2438         element == EL_QUICKSAND_FAST_EMPTYING ||
2439         element == EL_MAGIC_WALL_EMPTYING ||
2440         element == EL_BD_MAGIC_WALL_EMPTYING ||
2441         element == EL_DC_MAGIC_WALL_EMPTYING ||
2442         element == EL_AMOEBA_DROPPING)
2443       cut_mode = CUT_ABOVE;
2444     else if (element == EL_QUICKSAND_FILLING ||
2445              element == EL_QUICKSAND_FAST_FILLING ||
2446              element == EL_MAGIC_WALL_FILLING ||
2447              element == EL_BD_MAGIC_WALL_FILLING ||
2448              element == EL_DC_MAGIC_WALL_FILLING)
2449       cut_mode = CUT_BELOW;
2450
2451     if (cut_mode == CUT_ABOVE)
2452       DrawScreenElement(x, y, element);
2453     else
2454       DrawScreenElement(x, y, EL_EMPTY);
2455
2456     if (horiz_move)
2457       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2458     else if (cut_mode == NO_CUTTING)
2459       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2460     else
2461     {
2462       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2463
2464       if (cut_mode == CUT_BELOW &&
2465           IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2466         DrawLevelElement(lx, ly + 1, element);
2467     }
2468
2469     if (content == EL_ACID)
2470     {
2471       int dir = MovDir[lx][ly];
2472       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2473       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2474
2475       DrawLevelElementThruMask(newlx, newly, EL_ACID);
2476
2477       // prevent target field from being drawn again (but without masking)
2478       // (this would happen if target field is scanned after moving element)
2479       Stop[newlx][newly] = TRUE;
2480     }
2481   }
2482   else if (IS_BLOCKED(lx, ly))
2483   {
2484     int oldx, oldy;
2485     int sx, sy;
2486     int horiz_move;
2487     boolean cut_mode = NO_CUTTING;
2488     int element_old, content_old;
2489
2490     Blocked2Moving(lx, ly, &oldx, &oldy);
2491     sx = SCREENX(oldx);
2492     sy = SCREENY(oldy);
2493     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2494                   MovDir[oldx][oldy] == MV_RIGHT);
2495
2496     element_old = Feld[oldx][oldy];
2497     content_old = Store[oldx][oldy];
2498
2499     if (element_old == EL_QUICKSAND_EMPTYING ||
2500         element_old == EL_QUICKSAND_FAST_EMPTYING ||
2501         element_old == EL_MAGIC_WALL_EMPTYING ||
2502         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2503         element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2504         element_old == EL_AMOEBA_DROPPING)
2505       cut_mode = CUT_ABOVE;
2506
2507     DrawScreenElement(x, y, EL_EMPTY);
2508
2509     if (horiz_move)
2510       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2511                                NO_CUTTING);
2512     else if (cut_mode == NO_CUTTING)
2513       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2514                                cut_mode);
2515     else
2516       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2517                                cut_mode);
2518   }
2519   else if (IS_DRAWABLE(element))
2520     DrawScreenElement(x, y, element);
2521   else
2522     DrawScreenElement(x, y, EL_EMPTY);
2523 }
2524
2525 void DrawLevelField(int x, int y)
2526 {
2527   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2528     DrawScreenField(SCREENX(x), SCREENY(y));
2529   else if (IS_MOVING(x, y))
2530   {
2531     int newx,newy;
2532
2533     Moving2Blocked(x, y, &newx, &newy);
2534     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2535       DrawScreenField(SCREENX(newx), SCREENY(newy));
2536   }
2537   else if (IS_BLOCKED(x, y))
2538   {
2539     int oldx, oldy;
2540
2541     Blocked2Moving(x, y, &oldx, &oldy);
2542     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2543       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2544   }
2545 }
2546
2547 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2548                                 int (*el2img_function)(int), boolean masked,
2549                                 int element_bits_draw)
2550 {
2551   int element_base = map_mm_wall_element(element);
2552   int element_bits = (IS_DF_WALL(element) ?
2553                       element - EL_DF_WALL_START :
2554                       IS_MM_WALL(element) ?
2555                       element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2556   int graphic = el2img_function(element_base);
2557   int tilesize_draw = tilesize / 2;
2558   Bitmap *src_bitmap;
2559   int src_x, src_y;
2560   int i;
2561
2562   getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2563
2564   for (i = 0; i < 4; i++)
2565   {
2566     int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2567     int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2568
2569     if (!(element_bits_draw & (1 << i)))
2570       continue;
2571
2572     if (element_bits & (1 << i))
2573     {
2574       if (masked)
2575         BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2576                          tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2577       else
2578         BlitBitmap(src_bitmap, drawto, src_x, src_y,
2579                    tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2580     }
2581     else
2582     {
2583       if (!masked)
2584         ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2585                        tilesize_draw, tilesize_draw);
2586     }
2587   }
2588 }
2589
2590 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2591                            boolean masked, int element_bits_draw)
2592 {
2593   DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2594                       element, tilesize, el2edimg, masked, element_bits_draw);
2595 }
2596
2597 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2598                       int (*el2img_function)(int))
2599 {
2600   DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2601                       0x000f);
2602 }
2603
2604 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2605                          boolean masked)
2606 {
2607   if (IS_MM_WALL(element))
2608   {
2609     DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2610                         element, tilesize, el2edimg, masked, 0x000f);
2611   }
2612   else
2613   {
2614     int graphic = el2edimg(element);
2615
2616     if (masked)
2617       DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2618     else
2619       DrawSizedGraphic(x, y, graphic, 0, tilesize);
2620   }
2621 }
2622
2623 void DrawSizedElement(int x, int y, int element, int tilesize)
2624 {
2625   DrawSizedElementExt(x, y, element, tilesize, FALSE);
2626 }
2627
2628 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2629 {
2630   DrawSizedElementExt(x, y, element, tilesize, TRUE);
2631 }
2632
2633 void DrawMiniElement(int x, int y, int element)
2634 {
2635   int graphic;
2636
2637   graphic = el2edimg(element);
2638   DrawMiniGraphic(x, y, graphic);
2639 }
2640
2641 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2642                             int tilesize)
2643 {
2644   int x = sx + scroll_x, y = sy + scroll_y;
2645
2646   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2647     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2648   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2649     DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2650   else
2651     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2652 }
2653
2654 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2655 {
2656   int x = sx + scroll_x, y = sy + scroll_y;
2657
2658   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2659     DrawMiniElement(sx, sy, EL_EMPTY);
2660   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2661     DrawMiniElement(sx, sy, Feld[x][y]);
2662   else
2663     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2664 }
2665
2666 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2667                                  int x, int y, int xsize, int ysize,
2668                                  int tile_width, int tile_height)
2669 {
2670   Bitmap *src_bitmap;
2671   int src_x, src_y;
2672   int dst_x = startx + x * tile_width;
2673   int dst_y = starty + y * tile_height;
2674   int width  = graphic_info[graphic].width;
2675   int height = graphic_info[graphic].height;
2676   int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
2677   int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2678   int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
2679   int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2680   int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
2681   int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2682   boolean draw_masked = graphic_info[graphic].draw_masked;
2683
2684   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2685
2686   if (src_bitmap == NULL || width < tile_width || height < tile_height)
2687   {
2688     ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2689     return;
2690   }
2691
2692   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
2693             inner_sx + (x - 1) * tile_width  % inner_width);
2694   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2695             inner_sy + (y - 1) * tile_height % inner_height);
2696
2697   if (draw_masked)
2698     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2699                      dst_x, dst_y);
2700   else
2701     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2702                dst_x, dst_y);
2703 }
2704
2705 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2706                             int x, int y, int xsize, int ysize, int font_nr)
2707 {
2708   int font_width  = getFontWidth(font_nr);
2709   int font_height = getFontHeight(font_nr);
2710
2711   DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2712                               font_width, font_height);
2713 }
2714
2715 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2716 {
2717   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2718   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2719   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2720   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2721   boolean no_delay = (tape.warp_forward);
2722   unsigned int anim_delay = 0;
2723   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2724   int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2725   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2726   int font_width = getFontWidth(font_nr);
2727   int font_height = getFontHeight(font_nr);
2728   int max_xsize = level.envelope[envelope_nr].xsize;
2729   int max_ysize = level.envelope[envelope_nr].ysize;
2730   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2731   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2732   int xend = max_xsize;
2733   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2734   int xstep = (xstart < xend ? 1 : 0);
2735   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2736   int start = 0;
2737   int end = MAX(xend - xstart, yend - ystart);
2738   int i;
2739
2740   for (i = start; i <= end; i++)
2741   {
2742     int last_frame = end;       // last frame of this "for" loop
2743     int x = xstart + i * xstep;
2744     int y = ystart + i * ystep;
2745     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2746     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2747     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2748     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2749     int xx, yy;
2750
2751     SetDrawtoField(DRAW_TO_FIELDBUFFER);
2752
2753     BlitScreenToBitmap(backbuffer);
2754
2755     SetDrawtoField(DRAW_TO_BACKBUFFER);
2756
2757     for (yy = 0; yy < ysize; yy++)
2758       for (xx = 0; xx < xsize; xx++)
2759         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2760
2761     DrawTextBuffer(sx + font_width, sy + font_height,
2762                    level.envelope[envelope_nr].text, font_nr, max_xsize,
2763                    xsize - 2, ysize - 2, 0, mask_mode,
2764                    level.envelope[envelope_nr].autowrap,
2765                    level.envelope[envelope_nr].centered, FALSE);
2766
2767     redraw_mask |= REDRAW_FIELD;
2768     BackToFront();
2769
2770     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2771   }
2772 }
2773
2774 void ShowEnvelope(int envelope_nr)
2775 {
2776   int element = EL_ENVELOPE_1 + envelope_nr;
2777   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2778   int sound_opening = element_info[element].sound[ACTION_OPENING];
2779   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2780   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2781   boolean no_delay = (tape.warp_forward);
2782   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2783   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2784   int anim_mode = graphic_info[graphic].anim_mode;
2785   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2786                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2787
2788   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2789
2790   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2791
2792   if (anim_mode == ANIM_DEFAULT)
2793     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2794
2795   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2796
2797   if (tape.playing)
2798     Delay(wait_delay_value);
2799   else
2800     WaitForEventToContinue();
2801
2802   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2803
2804   if (anim_mode != ANIM_NONE)
2805     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2806
2807   if (anim_mode == ANIM_DEFAULT)
2808     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2809
2810   game.envelope_active = FALSE;
2811
2812   SetDrawtoField(DRAW_TO_FIELDBUFFER);
2813
2814   redraw_mask |= REDRAW_FIELD;
2815   BackToFront();
2816 }
2817
2818 static void setRequestBasePosition(int *x, int *y)
2819 {
2820   int sx_base, sy_base;
2821
2822   if (request.x != -1)
2823     sx_base = request.x;
2824   else if (request.align == ALIGN_LEFT)
2825     sx_base = SX;
2826   else if (request.align == ALIGN_RIGHT)
2827     sx_base = SX + SXSIZE;
2828   else
2829     sx_base = SX + SXSIZE / 2;
2830
2831   if (request.y != -1)
2832     sy_base = request.y;
2833   else if (request.valign == VALIGN_TOP)
2834     sy_base = SY;
2835   else if (request.valign == VALIGN_BOTTOM)
2836     sy_base = SY + SYSIZE;
2837   else
2838     sy_base = SY + SYSIZE / 2;
2839
2840   *x = sx_base;
2841   *y = sy_base;
2842 }
2843
2844 static void setRequestPositionExt(int *x, int *y, int width, int height,
2845                                   boolean add_border_size)
2846 {
2847   int border_size = request.border_size;
2848   int sx_base, sy_base;
2849   int sx, sy;
2850
2851   setRequestBasePosition(&sx_base, &sy_base);
2852
2853   if (request.align == ALIGN_LEFT)
2854     sx = sx_base;
2855   else if (request.align == ALIGN_RIGHT)
2856     sx = sx_base - width;
2857   else
2858     sx = sx_base - width  / 2;
2859
2860   if (request.valign == VALIGN_TOP)
2861     sy = sy_base;
2862   else if (request.valign == VALIGN_BOTTOM)
2863     sy = sy_base - height;
2864   else
2865     sy = sy_base - height / 2;
2866
2867   sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2868   sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2869
2870   if (add_border_size)
2871   {
2872     sx += border_size;
2873     sy += border_size;
2874   }
2875
2876   *x = sx;
2877   *y = sy;
2878 }
2879
2880 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2881 {
2882   setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2883 }
2884
2885 void DrawEnvelopeRequest(char *text)
2886 {
2887   char *text_final = text;
2888   char *text_door_style = NULL;
2889   int graphic = IMG_BACKGROUND_REQUEST;
2890   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2891   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2892   int font_nr = FONT_REQUEST;
2893   int font_width = getFontWidth(font_nr);
2894   int font_height = getFontHeight(font_nr);
2895   int border_size = request.border_size;
2896   int line_spacing = request.line_spacing;
2897   int line_height = font_height + line_spacing;
2898   int max_text_width  = request.width  - 2 * border_size;
2899   int max_text_height = request.height - 2 * border_size;
2900   int line_length = max_text_width  / font_width;
2901   int max_lines   = max_text_height / line_height;
2902   int text_width = line_length * font_width;
2903   int width = request.width;
2904   int height = request.height;
2905   int tile_size = MAX(request.step_offset, 1);
2906   int x_steps = width  / tile_size;
2907   int y_steps = height / tile_size;
2908   int sx_offset = border_size;
2909   int sy_offset = border_size;
2910   int sx, sy;
2911   int i, x, y;
2912
2913   if (request.centered)
2914     sx_offset = (request.width - text_width) / 2;
2915
2916   if (request.wrap_single_words && !request.autowrap)
2917   {
2918     char *src_text_ptr, *dst_text_ptr;
2919
2920     text_door_style = checked_malloc(2 * strlen(text) + 1);
2921
2922     src_text_ptr = text;
2923     dst_text_ptr = text_door_style;
2924
2925     while (*src_text_ptr)
2926     {
2927       if (*src_text_ptr == ' ' ||
2928           *src_text_ptr == '?' ||
2929           *src_text_ptr == '!')
2930         *dst_text_ptr++ = '\n';
2931
2932       if (*src_text_ptr != ' ')
2933         *dst_text_ptr++ = *src_text_ptr;
2934
2935       src_text_ptr++;
2936     }
2937
2938     *dst_text_ptr = '\0';
2939
2940     text_final = text_door_style;
2941   }
2942
2943   setRequestPosition(&sx, &sy, FALSE);
2944
2945   ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2946
2947   for (y = 0; y < y_steps; y++)
2948     for (x = 0; x < x_steps; x++)
2949       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2950                                   x, y, x_steps, y_steps,
2951                                   tile_size, tile_size);
2952
2953   /* force DOOR font inside door area */
2954   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2955
2956   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2957                  line_length, -1, max_lines, line_spacing, mask_mode,
2958                  request.autowrap, request.centered, FALSE);
2959
2960   ResetFontStatus();
2961
2962   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2963     RedrawGadget(tool_gadget[i]);
2964
2965   // store readily prepared envelope request for later use when animating
2966   BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2967
2968   if (text_door_style)
2969     free(text_door_style);
2970 }
2971
2972 void AnimateEnvelopeRequest(int anim_mode, int action)
2973 {
2974   int graphic = IMG_BACKGROUND_REQUEST;
2975   boolean draw_masked = graphic_info[graphic].draw_masked;
2976   int delay_value_normal = request.step_delay;
2977   int delay_value_fast = delay_value_normal / 2;
2978   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2979   boolean no_delay = (tape.warp_forward);
2980   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2981   int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2982   unsigned int anim_delay = 0;
2983
2984   int tile_size = MAX(request.step_offset, 1);
2985   int max_xsize = request.width  / tile_size;
2986   int max_ysize = request.height / tile_size;
2987   int max_xsize_inner = max_xsize - 2;
2988   int max_ysize_inner = max_ysize - 2;
2989
2990   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2991   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2992   int xend = max_xsize_inner;
2993   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2994   int xstep = (xstart < xend ? 1 : 0);
2995   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2996   int start = 0;
2997   int end = MAX(xend - xstart, yend - ystart);
2998   int i;
2999
3000   if (setup.quick_doors)
3001   {
3002     xstart = xend;
3003     ystart = yend;
3004     end = 0;
3005   }
3006
3007   for (i = start; i <= end; i++)
3008   {
3009     int last_frame = end;       // last frame of this "for" loop
3010     int x = xstart + i * xstep;
3011     int y = ystart + i * ystep;
3012     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3013     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3014     int xsize_size_left = (xsize - 1) * tile_size;
3015     int ysize_size_top  = (ysize - 1) * tile_size;
3016     int max_xsize_pos = (max_xsize - 1) * tile_size;
3017     int max_ysize_pos = (max_ysize - 1) * tile_size;
3018     int width  = xsize * tile_size;
3019     int height = ysize * tile_size;
3020     int src_x, src_y;
3021     int dst_x, dst_y;
3022     int xx, yy;
3023
3024     setRequestPosition(&src_x, &src_y, FALSE);
3025     setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3026
3027     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3028
3029     for (yy = 0; yy < 2; yy++)
3030     {
3031       for (xx = 0; xx < 2; xx++)
3032       {
3033         int src_xx = src_x + xx * max_xsize_pos;
3034         int src_yy = src_y + yy * max_ysize_pos;
3035         int dst_xx = dst_x + xx * xsize_size_left;
3036         int dst_yy = dst_y + yy * ysize_size_top;
3037         int xx_size = (xx ? tile_size : xsize_size_left);
3038         int yy_size = (yy ? tile_size : ysize_size_top);
3039
3040         if (draw_masked)
3041           BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3042                            src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3043         else
3044           BlitBitmap(bitmap_db_store_2, backbuffer,
3045                      src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3046       }
3047     }
3048
3049     redraw_mask |= REDRAW_FIELD;
3050
3051     BackToFront();
3052
3053     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3054   }
3055 }
3056
3057 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3058 {
3059   int graphic = IMG_BACKGROUND_REQUEST;
3060   int sound_opening = SND_REQUEST_OPENING;
3061   int sound_closing = SND_REQUEST_CLOSING;
3062   int anim_mode_1 = request.anim_mode;                  /* (higher priority) */
3063   int anim_mode_2 = graphic_info[graphic].anim_mode;    /* (lower priority) */
3064   int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3065   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3066                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3067
3068   if (game_status == GAME_MODE_PLAYING)
3069     BlitScreenToBitmap(backbuffer);
3070
3071   SetDrawtoField(DRAW_TO_BACKBUFFER);
3072
3073   // SetDrawBackgroundMask(REDRAW_NONE);
3074
3075   if (action == ACTION_OPENING)
3076   {
3077     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3078
3079     if (req_state & REQ_ASK)
3080     {
3081       MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3082       MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3083     }
3084     else if (req_state & REQ_CONFIRM)
3085     {
3086       MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3087     }
3088     else if (req_state & REQ_PLAYER)
3089     {
3090       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3091       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3092       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3093       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3094     }
3095
3096     DrawEnvelopeRequest(text);
3097   }
3098
3099   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
3100
3101   if (action == ACTION_OPENING)
3102   {
3103     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3104
3105     if (anim_mode == ANIM_DEFAULT)
3106       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3107
3108     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3109   }
3110   else
3111   {
3112     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3113
3114     if (anim_mode != ANIM_NONE)
3115       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3116
3117     if (anim_mode == ANIM_DEFAULT)
3118       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3119   }
3120
3121   game.envelope_active = FALSE;
3122
3123   if (action == ACTION_CLOSING)
3124     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3125
3126   // SetDrawBackgroundMask(last_draw_background_mask);
3127
3128   redraw_mask |= REDRAW_FIELD;
3129
3130   BackToFront();
3131
3132   if (action == ACTION_CLOSING &&
3133       game_status == GAME_MODE_PLAYING &&
3134       level.game_engine_type == GAME_ENGINE_TYPE_RND)
3135     SetDrawtoField(DRAW_TO_FIELDBUFFER);
3136 }
3137
3138 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3139 {
3140   if (IS_MM_WALL(element))
3141   {
3142     DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3143   }
3144   else
3145   {
3146     Bitmap *src_bitmap;
3147     int src_x, src_y;
3148     int graphic = el2preimg(element);
3149
3150     getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3151     BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3152                dst_x, dst_y);
3153   }
3154 }
3155
3156 void DrawLevel(int draw_background_mask)
3157 {
3158   int x,y;
3159
3160   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3161   SetDrawBackgroundMask(draw_background_mask);
3162
3163   ClearField();
3164
3165   for (x = BX1; x <= BX2; x++)
3166     for (y = BY1; y <= BY2; y++)
3167       DrawScreenField(x, y);
3168
3169   redraw_mask |= REDRAW_FIELD;
3170 }
3171
3172 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3173                     int tilesize)
3174 {
3175   int x,y;
3176
3177   for (x = 0; x < size_x; x++)
3178     for (y = 0; y < size_y; y++)
3179       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3180
3181   redraw_mask |= REDRAW_FIELD;
3182 }
3183
3184 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3185 {
3186   int x,y;
3187
3188   for (x = 0; x < size_x; x++)
3189     for (y = 0; y < size_y; y++)
3190       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3191
3192   redraw_mask |= REDRAW_FIELD;
3193 }
3194
3195 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3196 {
3197   boolean show_level_border = (BorderElement != EL_EMPTY);
3198   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3199   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3200   int tile_size = preview.tile_size;
3201   int preview_width  = preview.xsize * tile_size;
3202   int preview_height = preview.ysize * tile_size;
3203   int real_preview_xsize = MIN(level_xsize, preview.xsize);
3204   int real_preview_ysize = MIN(level_ysize, preview.ysize);
3205   int real_preview_width  = real_preview_xsize * tile_size;
3206   int real_preview_height = real_preview_ysize * tile_size;
3207   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3208   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3209   int x, y;
3210
3211   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3212     return;
3213
3214   DrawBackground(dst_x, dst_y, preview_width, preview_height);
3215
3216   dst_x += (preview_width  - real_preview_width)  / 2;
3217   dst_y += (preview_height - real_preview_height) / 2;
3218
3219   for (x = 0; x < real_preview_xsize; x++)
3220   {
3221     for (y = 0; y < real_preview_ysize; y++)
3222     {
3223       int lx = from_x + x + (show_level_border ? -1 : 0);
3224       int ly = from_y + y + (show_level_border ? -1 : 0);
3225       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3226                      getBorderElement(lx, ly));
3227
3228       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3229                          element, tile_size);
3230     }
3231   }
3232
3233   redraw_mask |= REDRAW_FIELD;
3234 }
3235
3236 #define MICROLABEL_EMPTY                0
3237 #define MICROLABEL_LEVEL_NAME           1
3238 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
3239 #define MICROLABEL_LEVEL_AUTHOR         3
3240 #define MICROLABEL_IMPORTED_FROM_HEAD   4
3241 #define MICROLABEL_IMPORTED_FROM        5
3242 #define MICROLABEL_IMPORTED_BY_HEAD     6
3243 #define MICROLABEL_IMPORTED_BY          7
3244
3245 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3246 {
3247   int max_text_width = SXSIZE;
3248   int font_width = getFontWidth(font_nr);
3249
3250   if (pos->align == ALIGN_CENTER)
3251     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3252   else if (pos->align == ALIGN_RIGHT)
3253     max_text_width = pos->x;
3254   else
3255     max_text_width = SXSIZE - pos->x;
3256
3257   return max_text_width / font_width;
3258 }
3259
3260 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3261 {
3262   char label_text[MAX_OUTPUT_LINESIZE + 1];
3263   int max_len_label_text;
3264   int font_nr = pos->font;
3265   int i;
3266
3267   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3268     return;
3269
3270   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3271       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3272       mode == MICROLABEL_IMPORTED_BY_HEAD)
3273     font_nr = pos->font_alt;
3274
3275   max_len_label_text = getMaxTextLength(pos, font_nr);
3276
3277   if (pos->size != -1)
3278     max_len_label_text = pos->size;
3279
3280   for (i = 0; i < max_len_label_text; i++)
3281     label_text[i] = ' ';
3282   label_text[max_len_label_text] = '\0';
3283
3284   if (strlen(label_text) > 0)
3285     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3286
3287   strncpy(label_text,
3288           (mode == MICROLABEL_LEVEL_NAME ? level.name :
3289            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3290            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3291            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3292            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3293            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3294            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3295           max_len_label_text);
3296   label_text[max_len_label_text] = '\0';
3297
3298   if (strlen(label_text) > 0)
3299     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3300
3301   redraw_mask |= REDRAW_FIELD;
3302 }
3303
3304 static void DrawPreviewLevelLabel(int mode)
3305 {
3306   DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3307 }
3308
3309 static void DrawPreviewLevelInfo(int mode)
3310 {
3311   if (mode == MICROLABEL_LEVEL_NAME)
3312     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3313   else if (mode == MICROLABEL_LEVEL_AUTHOR)
3314     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3315 }
3316
3317 static void DrawPreviewLevelExt(boolean restart)
3318 {
3319   static unsigned int scroll_delay = 0;
3320   static unsigned int label_delay = 0;
3321   static int from_x, from_y, scroll_direction;
3322   static int label_state, label_counter;
3323   unsigned int scroll_delay_value = preview.step_delay;
3324   boolean show_level_border = (BorderElement != EL_EMPTY);
3325   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3326   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3327
3328   if (restart)
3329   {
3330     from_x = 0;
3331     from_y = 0;
3332
3333     if (preview.anim_mode == ANIM_CENTERED)
3334     {
3335       if (level_xsize > preview.xsize)
3336         from_x = (level_xsize - preview.xsize) / 2;
3337       if (level_ysize > preview.ysize)
3338         from_y = (level_ysize - preview.ysize) / 2;
3339     }
3340
3341     from_x += preview.xoffset;
3342     from_y += preview.yoffset;
3343
3344     scroll_direction = MV_RIGHT;
3345     label_state = 1;
3346     label_counter = 0;
3347
3348     DrawPreviewLevelPlayfield(from_x, from_y);
3349     DrawPreviewLevelLabel(label_state);
3350
3351     DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3352     DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3353
3354     /* initialize delay counters */
3355     DelayReached(&scroll_delay, 0);
3356     DelayReached(&label_delay, 0);
3357
3358     if (leveldir_current->name)
3359     {
3360       struct TextPosInfo *pos = &menu.main.text.level_info_1;
3361       char label_text[MAX_OUTPUT_LINESIZE + 1];
3362       int font_nr = pos->font;
3363       int max_len_label_text = getMaxTextLength(pos, font_nr);
3364
3365       if (pos->size != -1)
3366         max_len_label_text = pos->size;
3367
3368       strncpy(label_text, leveldir_current->name, max_len_label_text);
3369       label_text[max_len_label_text] = '\0';
3370
3371       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3372         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3373     }
3374
3375     return;
3376   }
3377
3378   /* scroll preview level, if needed */
3379   if (preview.anim_mode != ANIM_NONE &&
3380       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3381       DelayReached(&scroll_delay, scroll_delay_value))
3382   {
3383     switch (scroll_direction)
3384     {
3385       case MV_LEFT:
3386         if (from_x > 0)
3387         {
3388           from_x -= preview.step_offset;
3389           from_x = (from_x < 0 ? 0 : from_x);
3390         }
3391         else
3392           scroll_direction = MV_UP;
3393         break;
3394
3395       case MV_RIGHT:
3396         if (from_x < level_xsize - preview.xsize)
3397         {
3398           from_x += preview.step_offset;
3399           from_x = (from_x > level_xsize - preview.xsize ?
3400                     level_xsize - preview.xsize : from_x);
3401         }
3402         else
3403           scroll_direction = MV_DOWN;
3404         break;
3405
3406       case MV_UP:
3407         if (from_y > 0)
3408         {
3409           from_y -= preview.step_offset;
3410           from_y = (from_y < 0 ? 0 : from_y);
3411         }
3412         else
3413           scroll_direction = MV_RIGHT;
3414         break;
3415
3416       case MV_DOWN:
3417         if (from_y < level_ysize - preview.ysize)
3418         {
3419           from_y += preview.step_offset;
3420           from_y = (from_y > level_ysize - preview.ysize ?
3421                     level_ysize - preview.ysize : from_y);
3422         }
3423         else
3424           scroll_direction = MV_LEFT;
3425         break;
3426
3427       default:
3428         break;
3429     }
3430
3431     DrawPreviewLevelPlayfield(from_x, from_y);
3432   }
3433
3434   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3435   /* redraw micro level label, if needed */
3436   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3437       !strEqual(level.author, ANONYMOUS_NAME) &&
3438       !strEqual(level.author, leveldir_current->name) &&
3439       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3440   {
3441     int max_label_counter = 23;
3442
3443     if (leveldir_current->imported_from != NULL &&
3444         strlen(leveldir_current->imported_from) > 0)
3445       max_label_counter += 14;
3446     if (leveldir_current->imported_by != NULL &&
3447         strlen(leveldir_current->imported_by) > 0)
3448       max_label_counter += 14;
3449
3450     label_counter = (label_counter + 1) % max_label_counter;
3451     label_state = (label_counter >= 0 && label_counter <= 7 ?
3452                    MICROLABEL_LEVEL_NAME :
3453                    label_counter >= 9 && label_counter <= 12 ?
3454                    MICROLABEL_LEVEL_AUTHOR_HEAD :
3455                    label_counter >= 14 && label_counter <= 21 ?
3456                    MICROLABEL_LEVEL_AUTHOR :
3457                    label_counter >= 23 && label_counter <= 26 ?
3458                    MICROLABEL_IMPORTED_FROM_HEAD :
3459                    label_counter >= 28 && label_counter <= 35 ?
3460                    MICROLABEL_IMPORTED_FROM :
3461                    label_counter >= 37 && label_counter <= 40 ?
3462                    MICROLABEL_IMPORTED_BY_HEAD :
3463                    label_counter >= 42 && label_counter <= 49 ?
3464                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3465
3466     if (leveldir_current->imported_from == NULL &&
3467         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3468          label_state == MICROLABEL_IMPORTED_FROM))
3469       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3470                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3471
3472     DrawPreviewLevelLabel(label_state);
3473   }
3474 }
3475
3476 void DrawPreviewLevelInitial()
3477 {
3478   DrawPreviewLevelExt(TRUE);
3479 }
3480
3481 void DrawPreviewLevelAnimation()
3482 {
3483   DrawPreviewLevelExt(FALSE);
3484 }
3485
3486 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3487                                            int graphic, int sync_frame,
3488                                            int mask_mode)
3489 {
3490   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3491
3492   if (mask_mode == USE_MASKING)
3493     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3494   else
3495     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3496 }
3497
3498 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3499                                   int graphic, int sync_frame, int mask_mode)
3500 {
3501   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3502
3503   if (mask_mode == USE_MASKING)
3504     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3505   else
3506     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3507 }
3508
3509 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3510 {
3511   int lx = LEVELX(x), ly = LEVELY(y);
3512
3513   if (!IN_SCR_FIELD(x, y))
3514     return;
3515
3516   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3517                           graphic, GfxFrame[lx][ly], NO_MASKING);
3518
3519   MarkTileDirty(x, y);
3520 }
3521
3522 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3523 {
3524   int lx = LEVELX(x), ly = LEVELY(y);
3525
3526   if (!IN_SCR_FIELD(x, y))
3527     return;
3528
3529   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3530                           graphic, GfxFrame[lx][ly], NO_MASKING);
3531   MarkTileDirty(x, y);
3532 }
3533
3534 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3535 {
3536   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3537 }
3538
3539 void DrawLevelElementAnimation(int x, int y, int element)
3540 {
3541   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3542
3543   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3544 }
3545
3546 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3547 {
3548   int sx = SCREENX(x), sy = SCREENY(y);
3549
3550   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3551     return;
3552
3553   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3554     return;
3555
3556   DrawGraphicAnimation(sx, sy, graphic);
3557
3558 #if 1
3559   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3560     DrawLevelFieldCrumbled(x, y);
3561 #else
3562   if (GFX_CRUMBLED(Feld[x][y]))
3563     DrawLevelFieldCrumbled(x, y);
3564 #endif
3565 }
3566
3567 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3568 {
3569   int sx = SCREENX(x), sy = SCREENY(y);
3570   int graphic;
3571
3572   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3573     return;
3574
3575   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3576
3577   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3578     return;
3579
3580   DrawGraphicAnimation(sx, sy, graphic);
3581
3582   if (GFX_CRUMBLED(element))
3583     DrawLevelFieldCrumbled(x, y);
3584 }
3585
3586 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3587 {
3588   if (player->use_murphy)
3589   {
3590     /* this works only because currently only one player can be "murphy" ... */
3591     static int last_horizontal_dir = MV_LEFT;
3592     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3593
3594     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3595       last_horizontal_dir = move_dir;
3596
3597     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
3598     {
3599       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3600
3601       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3602     }
3603
3604     return graphic;
3605   }
3606   else
3607     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3608 }
3609
3610 static boolean equalGraphics(int graphic1, int graphic2)
3611 {
3612   struct GraphicInfo *g1 = &graphic_info[graphic1];
3613   struct GraphicInfo *g2 = &graphic_info[graphic2];
3614
3615   return (g1->bitmap      == g2->bitmap &&
3616           g1->src_x       == g2->src_x &&
3617           g1->src_y       == g2->src_y &&
3618           g1->anim_frames == g2->anim_frames &&
3619           g1->anim_delay  == g2->anim_delay &&
3620           g1->anim_mode   == g2->anim_mode);
3621 }
3622
3623 void DrawAllPlayers()
3624 {
3625   int i;
3626
3627   for (i = 0; i < MAX_PLAYERS; i++)
3628     if (stored_player[i].active)
3629       DrawPlayer(&stored_player[i]);
3630 }
3631
3632 void DrawPlayerField(int x, int y)
3633 {
3634   if (!IS_PLAYER(x, y))
3635     return;
3636
3637   DrawPlayer(PLAYERINFO(x, y));
3638 }
3639
3640 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3641
3642 void DrawPlayer(struct PlayerInfo *player)
3643 {
3644   int jx = player->jx;
3645   int jy = player->jy;
3646   int move_dir = player->MovDir;
3647   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3648   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
3649   int last_jx = (player->is_moving ? jx - dx : jx);
3650   int last_jy = (player->is_moving ? jy - dy : jy);
3651   int next_jx = jx + dx;
3652   int next_jy = jy + dy;
3653   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3654   boolean player_is_opaque = FALSE;
3655   int sx = SCREENX(jx), sy = SCREENY(jy);
3656   int sxx = 0, syy = 0;
3657   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3658   int graphic;
3659   int action = ACTION_DEFAULT;
3660   int last_player_graphic = getPlayerGraphic(player, move_dir);
3661   int last_player_frame = player->Frame;
3662   int frame = 0;
3663
3664   /* GfxElement[][] is set to the element the player is digging or collecting;
3665      remove also for off-screen player if the player is not moving anymore */
3666   if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3667     GfxElement[jx][jy] = EL_UNDEFINED;
3668
3669   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3670     return;
3671
3672 #if DEBUG
3673   if (!IN_LEV_FIELD(jx, jy))
3674   {
3675     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3676     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3677     printf("DrawPlayerField(): This should never happen!\n");
3678     return;
3679   }
3680 #endif
3681
3682   if (element == EL_EXPLOSION)
3683     return;
3684
3685   action = (player->is_pushing    ? ACTION_PUSHING         :
3686             player->is_digging    ? ACTION_DIGGING         :
3687             player->is_collecting ? ACTION_COLLECTING      :
3688             player->is_moving     ? ACTION_MOVING          :
3689             player->is_snapping   ? ACTION_SNAPPING        :
3690             player->is_dropping   ? ACTION_DROPPING        :
3691             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
3692
3693   if (player->is_waiting)
3694     move_dir = player->dir_waiting;
3695
3696   InitPlayerGfxAnimation(player, action, move_dir);
3697
3698   /* ----------------------------------------------------------------------- */
3699   /* draw things in the field the player is leaving, if needed               */
3700   /* ----------------------------------------------------------------------- */
3701
3702   if (player->is_moving)
3703   {
3704     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3705     {
3706       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3707
3708       if (last_element == EL_DYNAMITE_ACTIVE ||
3709           last_element == EL_EM_DYNAMITE_ACTIVE ||
3710           last_element == EL_SP_DISK_RED_ACTIVE)
3711         DrawDynamite(last_jx, last_jy);
3712       else
3713         DrawLevelFieldThruMask(last_jx, last_jy);
3714     }
3715     else if (last_element == EL_DYNAMITE_ACTIVE ||
3716              last_element == EL_EM_DYNAMITE_ACTIVE ||
3717              last_element == EL_SP_DISK_RED_ACTIVE)
3718       DrawDynamite(last_jx, last_jy);
3719 #if 0
3720     /* !!! this is not enough to prevent flickering of players which are
3721        moving next to each others without a free tile between them -- this
3722        can only be solved by drawing all players layer by layer (first the
3723        background, then the foreground etc.) !!! => TODO */
3724     else if (!IS_PLAYER(last_jx, last_jy))
3725       DrawLevelField(last_jx, last_jy);
3726 #else
3727     else
3728       DrawLevelField(last_jx, last_jy);
3729 #endif
3730
3731     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3732       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3733   }
3734
3735   if (!IN_SCR_FIELD(sx, sy))
3736     return;
3737
3738   /* ----------------------------------------------------------------------- */
3739   /* draw things behind the player, if needed                                */
3740   /* ----------------------------------------------------------------------- */
3741
3742   if (Back[jx][jy])
3743     DrawLevelElement(jx, jy, Back[jx][jy]);
3744   else if (IS_ACTIVE_BOMB(element))
3745     DrawLevelElement(jx, jy, EL_EMPTY);
3746   else
3747   {
3748     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3749     {
3750       int old_element = GfxElement[jx][jy];
3751       int old_graphic = el_act_dir2img(old_element, action, move_dir);
3752       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3753
3754       if (GFX_CRUMBLED(old_element))
3755         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3756       else
3757         DrawGraphic(sx, sy, old_graphic, frame);
3758
3759       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3760         player_is_opaque = TRUE;
3761     }
3762     else
3763     {
3764       GfxElement[jx][jy] = EL_UNDEFINED;
3765
3766       /* make sure that pushed elements are drawn with correct frame rate */
3767       graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3768
3769       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3770         GfxFrame[jx][jy] = player->StepFrame;
3771
3772       DrawLevelField(jx, jy);
3773     }
3774   }
3775
3776 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3777   /* ----------------------------------------------------------------------- */
3778   /* draw player himself                                                     */
3779   /* ----------------------------------------------------------------------- */
3780
3781   graphic = getPlayerGraphic(player, move_dir);
3782
3783   /* in the case of changed player action or direction, prevent the current
3784      animation frame from being restarted for identical animations */
3785   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3786     player->Frame = last_player_frame;
3787
3788   frame = getGraphicAnimationFrame(graphic, player->Frame);
3789
3790   if (player->GfxPos)
3791   {
3792     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3793       sxx = player->GfxPos;
3794     else
3795       syy = player->GfxPos;
3796   }
3797
3798   if (player_is_opaque)
3799     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3800   else
3801     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3802
3803   if (SHIELD_ON(player))
3804   {
3805     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3806                    IMG_SHIELD_NORMAL_ACTIVE);
3807     int frame = getGraphicAnimationFrame(graphic, -1);
3808
3809     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3810   }
3811 #endif
3812
3813 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3814   if (player->GfxPos)
3815   {
3816     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3817       sxx = player->GfxPos;
3818     else
3819       syy = player->GfxPos;
3820   }
3821 #endif
3822
3823   /* ----------------------------------------------------------------------- */
3824   /* draw things the player is pushing, if needed                            */
3825   /* ----------------------------------------------------------------------- */
3826
3827   if (player->is_pushing && player->is_moving)
3828   {
3829     int px = SCREENX(jx), py = SCREENY(jy);
3830     int pxx = (TILEX - ABS(sxx)) * dx;
3831     int pyy = (TILEY - ABS(syy)) * dy;
3832     int gfx_frame = GfxFrame[jx][jy];
3833
3834     int graphic;
3835     int sync_frame;
3836     int frame;
3837
3838     if (!IS_MOVING(jx, jy))             /* push movement already finished */
3839     {
3840       element = Feld[next_jx][next_jy];
3841       gfx_frame = GfxFrame[next_jx][next_jy];
3842     }
3843
3844     graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3845
3846     sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3847     frame = getGraphicAnimationFrame(graphic, sync_frame);
3848
3849     /* draw background element under pushed element (like the Sokoban field) */
3850     if (game.use_masked_pushing && IS_MOVING(jx, jy))
3851     {
3852       /* this allows transparent pushing animation over non-black background */
3853
3854       if (Back[jx][jy])
3855         DrawLevelElement(jx, jy, Back[jx][jy]);
3856       else
3857         DrawLevelElement(jx, jy, EL_EMPTY);
3858
3859       if (Back[next_jx][next_jy])
3860         DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3861       else
3862         DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3863     }
3864     else if (Back[next_jx][next_jy])
3865       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3866
3867 #if 1
3868     /* do not draw (EM style) pushing animation when pushing is finished */
3869     /* (two-tile animations usually do not contain start and end frame) */
3870     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3871       DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3872     else
3873       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3874 #else
3875     /* masked drawing is needed for EMC style (double) movement graphics */
3876     /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3877     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3878 #endif
3879   }
3880
3881 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3882   /* ----------------------------------------------------------------------- */
3883   /* draw player himself                                                     */
3884   /* ----------------------------------------------------------------------- */
3885
3886   graphic = getPlayerGraphic(player, move_dir);
3887
3888   /* in the case of changed player action or direction, prevent the current
3889      animation frame from being restarted for identical animations */
3890   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3891     player->Frame = last_player_frame;
3892
3893   frame = getGraphicAnimationFrame(graphic, player->Frame);
3894
3895   if (player->GfxPos)
3896   {
3897     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3898       sxx = player->GfxPos;
3899     else
3900       syy = player->GfxPos;
3901   }
3902
3903   if (player_is_opaque)
3904     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3905   else
3906     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3907
3908   if (SHIELD_ON(player))
3909   {
3910     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3911                    IMG_SHIELD_NORMAL_ACTIVE);
3912     int frame = getGraphicAnimationFrame(graphic, -1);
3913
3914     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3915   }
3916 #endif
3917
3918   /* ----------------------------------------------------------------------- */
3919   /* draw things in front of player (active dynamite or dynabombs)           */
3920   /* ----------------------------------------------------------------------- */
3921
3922   if (IS_ACTIVE_BOMB(element))
3923   {
3924     graphic = el2img(element);
3925     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3926
3927     if (game.emulation == EMU_SUPAPLEX)
3928       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3929     else
3930       DrawGraphicThruMask(sx, sy, graphic, frame);
3931   }
3932
3933   if (player_is_moving && last_element == EL_EXPLOSION)
3934   {
3935     int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3936                    GfxElement[last_jx][last_jy] :  EL_EMPTY);
3937     int graphic = el_act2img(element, ACTION_EXPLODING);
3938     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3939     int phase = ExplodePhase[last_jx][last_jy] - 1;
3940     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3941
3942     if (phase >= delay)
3943       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3944   }
3945
3946   /* ----------------------------------------------------------------------- */
3947   /* draw elements the player is just walking/passing through/under          */
3948   /* ----------------------------------------------------------------------- */
3949
3950   if (player_is_moving)
3951   {
3952     /* handle the field the player is leaving ... */
3953     if (IS_ACCESSIBLE_INSIDE(last_element))
3954       DrawLevelField(last_jx, last_jy);
3955     else if (IS_ACCESSIBLE_UNDER(last_element))
3956       DrawLevelFieldThruMask(last_jx, last_jy);
3957   }
3958
3959   /* do not redraw accessible elements if the player is just pushing them */
3960   if (!player_is_moving || !player->is_pushing)
3961   {
3962     /* ... and the field the player is entering */
3963     if (IS_ACCESSIBLE_INSIDE(element))
3964       DrawLevelField(jx, jy);
3965     else if (IS_ACCESSIBLE_UNDER(element))
3966       DrawLevelFieldThruMask(jx, jy);
3967   }
3968
3969   MarkTileDirty(sx, sy);
3970 }
3971
3972 /* ------------------------------------------------------------------------- */
3973
3974 void WaitForEventToContinue()
3975 {
3976   boolean still_wait = TRUE;
3977
3978   if (program.headless)
3979     return;
3980
3981   /* simulate releasing mouse button over last gadget, if still pressed */
3982   if (button_status)
3983     HandleGadgets(-1, -1, 0);
3984
3985   button_status = MB_RELEASED;
3986
3987   ClearEventQueue();
3988
3989   while (still_wait)
3990   {
3991     Event event;
3992
3993     if (NextValidEvent(&event))
3994     {
3995       switch (event.type)
3996       {
3997         case EVENT_BUTTONPRESS:
3998         case EVENT_KEYPRESS:
3999 #if defined(TARGET_SDL2)
4000         case SDL_CONTROLLERBUTTONDOWN:
4001 #endif
4002         case SDL_JOYBUTTONDOWN:
4003           still_wait = FALSE;
4004           break;
4005
4006         case EVENT_KEYRELEASE:
4007           ClearPlayerAction();
4008           break;
4009
4010         default:
4011           HandleOtherEvents(&event);
4012           break;
4013       }
4014     }
4015     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4016     {
4017       still_wait = FALSE;
4018     }
4019
4020     BackToFront();
4021   }
4022 }
4023
4024 #define MAX_REQUEST_LINES               13
4025 #define MAX_REQUEST_LINE_FONT1_LEN      7
4026 #define MAX_REQUEST_LINE_FONT2_LEN      10
4027
4028 static int RequestHandleEvents(unsigned int req_state)
4029 {
4030   boolean level_solved = (game_status == GAME_MODE_PLAYING &&
4031                           local_player->LevelSolved_GameEnd);
4032   int width  = request.width;
4033   int height = request.height;
4034   int sx, sy;
4035   int result;
4036
4037   setRequestPosition(&sx, &sy, FALSE);
4038
4039   button_status = MB_RELEASED;
4040
4041   request_gadget_id = -1;
4042   result = -1;
4043
4044   while (result < 0)
4045   {
4046     if (level_solved)
4047     {
4048       /* the MM game engine does not use a special (scrollable) field buffer */
4049       if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4050         SetDrawtoField(DRAW_TO_FIELDBUFFER);
4051
4052       HandleGameActions();
4053
4054       SetDrawtoField(DRAW_TO_BACKBUFFER);
4055
4056       if (global.use_envelope_request)
4057       {
4058         /* copy current state of request area to middle of playfield area */
4059         BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4060       }
4061     }
4062
4063     if (PendingEvent())
4064     {
4065       Event event;
4066
4067       while (NextValidEvent(&event))
4068       {
4069         switch (event.type)
4070         {
4071           case EVENT_BUTTONPRESS:
4072           case EVENT_BUTTONRELEASE:
4073           case EVENT_MOTIONNOTIFY:
4074           {
4075             int mx, my;
4076
4077             if (event.type == EVENT_MOTIONNOTIFY)
4078             {
4079               if (!button_status)
4080                 continue;
4081
4082               motion_status = TRUE;
4083               mx = ((MotionEvent *) &event)->x;
4084               my = ((MotionEvent *) &event)->y;
4085             }
4086             else
4087             {
4088               motion_status = FALSE;
4089               mx = ((ButtonEvent *) &event)->x;
4090               my = ((ButtonEvent *) &event)->y;
4091               if (event.type == EVENT_BUTTONPRESS)
4092                 button_status = ((ButtonEvent *) &event)->button;
4093               else
4094                 button_status = MB_RELEASED;
4095             }
4096
4097             /* this sets 'request_gadget_id' */
4098             HandleGadgets(mx, my, button_status);
4099
4100             switch (request_gadget_id)
4101             {
4102               case TOOL_CTRL_ID_YES:
4103                 result = TRUE;
4104                 break;
4105               case TOOL_CTRL_ID_NO:
4106                 result = FALSE;
4107                 break;
4108               case TOOL_CTRL_ID_CONFIRM:
4109                 result = TRUE | FALSE;
4110                 break;
4111
4112               case TOOL_CTRL_ID_PLAYER_1:
4113                 result = 1;
4114                 break;
4115               case TOOL_CTRL_ID_PLAYER_2:
4116                 result = 2;
4117                 break;
4118               case TOOL_CTRL_ID_PLAYER_3:
4119                 result = 3;
4120                 break;
4121               case TOOL_CTRL_ID_PLAYER_4:
4122                 result = 4;
4123                 break;
4124
4125               default:
4126                 break;
4127             }
4128
4129             break;
4130           }
4131
4132 #if defined(TARGET_SDL2)
4133           case SDL_WINDOWEVENT:
4134             HandleWindowEvent((WindowEvent *) &event);
4135             break;
4136
4137           case SDL_APP_WILLENTERBACKGROUND:
4138           case SDL_APP_DIDENTERBACKGROUND:
4139           case SDL_APP_WILLENTERFOREGROUND:
4140           case SDL_APP_DIDENTERFOREGROUND:
4141             HandlePauseResumeEvent((PauseResumeEvent *) &event);
4142             break;
4143 #endif
4144
4145           case EVENT_KEYPRESS:
4146           {
4147             Key key = GetEventKey((KeyEvent *)&event, TRUE);
4148
4149             switch (key)
4150             {
4151               case KSYM_space:
4152                 if (req_state & REQ_CONFIRM)
4153                   result = 1;
4154                 break;
4155
4156               case KSYM_Return:
4157 #if defined(TARGET_SDL2)
4158               case KSYM_Select:
4159               case KSYM_Menu:
4160 #if defined(KSYM_Rewind)
4161               case KSYM_Rewind:         /* for Amazon Fire TV remote */
4162 #endif
4163 #endif
4164                 result = 1;
4165                 break;
4166
4167               case KSYM_Escape:
4168 #if defined(TARGET_SDL2)
4169               case KSYM_Back:
4170 #if defined(KSYM_FastForward)
4171               case KSYM_FastForward:    /* for Amazon Fire TV remote */
4172 #endif
4173 #endif
4174                 result = 0;
4175                 break;
4176
4177               default:
4178                 HandleKeysDebug(key);
4179                 break;
4180             }
4181
4182             if (req_state & REQ_PLAYER)
4183               result = 0;
4184
4185             break;
4186           }
4187
4188           case EVENT_KEYRELEASE:
4189             ClearPlayerAction();
4190             break;
4191
4192 #if defined(TARGET_SDL2)
4193           case SDL_CONTROLLERBUTTONDOWN:
4194             switch (event.cbutton.button)
4195             {
4196               case SDL_CONTROLLER_BUTTON_A:
4197               case SDL_CONTROLLER_BUTTON_X:
4198               case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4199                 result = 1;
4200                 break;
4201
4202               case SDL_CONTROLLER_BUTTON_B:
4203               case SDL_CONTROLLER_BUTTON_Y:
4204               case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4205               case SDL_CONTROLLER_BUTTON_BACK:
4206                 result = 0;
4207                 break;
4208             }
4209
4210             if (req_state & REQ_PLAYER)
4211               result = 0;
4212
4213             break;
4214
4215           case SDL_CONTROLLERBUTTONUP:
4216             HandleJoystickEvent(&event);
4217             ClearPlayerAction();
4218             break;
4219 #endif
4220
4221           default:
4222             HandleOtherEvents(&event);
4223             break;
4224         }
4225       }
4226     }
4227     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4228     {
4229       int joy = AnyJoystick();
4230
4231       if (joy & JOY_BUTTON_1)
4232         result = 1;
4233       else if (joy & JOY_BUTTON_2)
4234         result = 0;
4235     }
4236
4237     if (level_solved)
4238     {
4239       if (global.use_envelope_request)
4240       {
4241         /* copy back current state of pressed buttons inside request area */
4242         BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4243       }
4244     }
4245
4246     BackToFront();
4247   }
4248
4249   return result;
4250 }
4251
4252 static boolean RequestDoor(char *text, unsigned int req_state)
4253 {
4254   unsigned int old_door_state;
4255   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4256   int font_nr = FONT_TEXT_2;
4257   char *text_ptr;
4258   int result;
4259   int ty;
4260
4261   if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4262   {
4263     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4264     font_nr = FONT_TEXT_1;
4265   }
4266
4267   if (game_status == GAME_MODE_PLAYING)
4268     BlitScreenToBitmap(backbuffer);
4269
4270   /* disable deactivated drawing when quick-loading level tape recording */
4271   if (tape.playing && tape.deactivate_display)
4272     TapeDeactivateDisplayOff(TRUE);
4273
4274   SetMouseCursor(CURSOR_DEFAULT);
4275
4276 #if defined(NETWORK_AVALIABLE)
4277   /* pause network game while waiting for request to answer */
4278   if (options.network &&
4279       game_status == GAME_MODE_PLAYING &&
4280       req_state & REQUEST_WAIT_FOR_INPUT)
4281     SendToServer_PausePlaying();
4282 #endif
4283
4284   old_door_state = GetDoorState();
4285
4286   /* simulate releasing mouse button over last gadget, if still pressed */
4287   if (button_status)
4288     HandleGadgets(-1, -1, 0);
4289
4290   UnmapAllGadgets();
4291
4292   /* draw released gadget before proceeding */
4293   // BackToFront();
4294
4295   if (old_door_state & DOOR_OPEN_1)
4296   {
4297     CloseDoor(DOOR_CLOSE_1);
4298
4299     /* save old door content */
4300     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4301                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4302   }
4303
4304   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4305   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4306
4307   /* clear door drawing field */
4308   DrawBackground(DX, DY, DXSIZE, DYSIZE);
4309
4310   /* force DOOR font inside door area */
4311   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4312
4313   /* write text for request */
4314   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4315   {
4316     char text_line[max_request_line_len + 1];
4317     int tx, tl, tc = 0;
4318
4319     if (!*text_ptr)
4320       break;
4321
4322     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4323     {
4324       tc = *(text_ptr + tx);
4325       // if (!tc || tc == ' ')
4326       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4327         break;
4328     }
4329
4330     if ((tc == '?' || tc == '!') && tl == 0)
4331       tl = 1;
4332
4333     if (!tl)
4334     { 
4335       text_ptr++; 
4336       ty--; 
4337       continue; 
4338     }
4339
4340     strncpy(text_line, text_ptr, tl);
4341     text_line[tl] = 0;
4342
4343     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4344              DY + 8 + ty * (getFontHeight(font_nr) + 2),
4345              text_line, font_nr);
4346
4347     text_ptr += tl + (tc == ' ' ? 1 : 0);
4348     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4349   }
4350
4351   ResetFontStatus();
4352
4353   if (req_state & REQ_ASK)
4354   {
4355     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4356     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4357   }
4358   else if (req_state & REQ_CONFIRM)
4359   {
4360     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4361   }
4362   else if (req_state & REQ_PLAYER)
4363   {
4364     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4365     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4366     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4367     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4368   }
4369
4370   /* copy request gadgets to door backbuffer */
4371   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4372
4373   OpenDoor(DOOR_OPEN_1);
4374
4375   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4376   {
4377     if (game_status == GAME_MODE_PLAYING)
4378     {
4379       SetPanelBackground();
4380       SetDrawBackgroundMask(REDRAW_DOOR_1);
4381     }
4382     else
4383     {
4384       SetDrawBackgroundMask(REDRAW_FIELD);
4385     }
4386
4387     return FALSE;
4388   }
4389
4390   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4391
4392   // ---------- handle request buttons ----------
4393   result = RequestHandleEvents(req_state);
4394
4395   UnmapToolButtons();
4396
4397   if (!(req_state & REQ_STAY_OPEN))
4398   {
4399     CloseDoor(DOOR_CLOSE_1);
4400
4401     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4402         (req_state & REQ_REOPEN))
4403       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4404   }
4405
4406   RemapAllGadgets();
4407
4408   if (game_status == GAME_MODE_PLAYING)
4409   {
4410     SetPanelBackground();
4411     SetDrawBackgroundMask(REDRAW_DOOR_1);
4412   }
4413   else
4414   {
4415     SetDrawBackgroundMask(REDRAW_FIELD);
4416   }
4417
4418 #if defined(NETWORK_AVALIABLE)
4419   /* continue network game after request */
4420   if (options.network &&
4421       game_status == GAME_MODE_PLAYING &&
4422       req_state & REQUEST_WAIT_FOR_INPUT)
4423     SendToServer_ContinuePlaying();
4424 #endif
4425
4426   /* restore deactivated drawing when quick-loading level tape recording */
4427   if (tape.playing && tape.deactivate_display)
4428     TapeDeactivateDisplayOn();
4429
4430   return result;
4431 }
4432
4433 static boolean RequestEnvelope(char *text, unsigned int req_state)
4434 {
4435   int result;
4436
4437   if (game_status == GAME_MODE_PLAYING)
4438     BlitScreenToBitmap(backbuffer);
4439
4440   /* disable deactivated drawing when quick-loading level tape recording */
4441   if (tape.playing && tape.deactivate_display)
4442     TapeDeactivateDisplayOff(TRUE);
4443
4444   SetMouseCursor(CURSOR_DEFAULT);
4445
4446 #if defined(NETWORK_AVALIABLE)
4447   /* pause network game while waiting for request to answer */
4448   if (options.network &&
4449       game_status == GAME_MODE_PLAYING &&
4450       req_state & REQUEST_WAIT_FOR_INPUT)
4451     SendToServer_PausePlaying();
4452 #endif
4453
4454   /* simulate releasing mouse button over last gadget, if still pressed */
4455   if (button_status)
4456     HandleGadgets(-1, -1, 0);
4457
4458   UnmapAllGadgets();
4459
4460   // (replace with setting corresponding request background)
4461   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4462   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4463
4464   /* clear door drawing field */
4465   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4466
4467   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4468
4469   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4470   {
4471     if (game_status == GAME_MODE_PLAYING)
4472     {
4473       SetPanelBackground();
4474       SetDrawBackgroundMask(REDRAW_DOOR_1);
4475     }
4476     else
4477     {
4478       SetDrawBackgroundMask(REDRAW_FIELD);
4479     }
4480
4481     return FALSE;
4482   }
4483
4484   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4485
4486   // ---------- handle request buttons ----------
4487   result = RequestHandleEvents(req_state);
4488
4489   UnmapToolButtons();
4490
4491   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4492
4493   RemapAllGadgets();
4494
4495   if (game_status == GAME_MODE_PLAYING)
4496   {
4497     SetPanelBackground();
4498     SetDrawBackgroundMask(REDRAW_DOOR_1);
4499   }
4500   else
4501   {
4502     SetDrawBackgroundMask(REDRAW_FIELD);
4503   }
4504
4505 #if defined(NETWORK_AVALIABLE)
4506   /* continue network game after request */
4507   if (options.network &&
4508       game_status == GAME_MODE_PLAYING &&
4509       req_state & REQUEST_WAIT_FOR_INPUT)
4510     SendToServer_ContinuePlaying();
4511 #endif
4512
4513   /* restore deactivated drawing when quick-loading level tape recording */
4514   if (tape.playing && tape.deactivate_display)
4515     TapeDeactivateDisplayOn();
4516
4517   return result;
4518 }
4519
4520 boolean Request(char *text, unsigned int req_state)
4521 {
4522   boolean overlay_active = GetOverlayActive();
4523   boolean result;
4524
4525   SetOverlayActive(FALSE);
4526
4527   if (global.use_envelope_request)
4528     result = RequestEnvelope(text, req_state);
4529   else
4530     result = RequestDoor(text, req_state);
4531
4532   SetOverlayActive(overlay_active);
4533
4534   return result;
4535 }
4536
4537 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4538 {
4539   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4540   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4541   int compare_result;
4542
4543   if (dpo1->sort_priority != dpo2->sort_priority)
4544     compare_result = dpo1->sort_priority - dpo2->sort_priority;
4545   else
4546     compare_result = dpo1->nr - dpo2->nr;
4547
4548   return compare_result;
4549 }
4550
4551 void InitGraphicCompatibilityInfo_Doors()
4552 {
4553   struct
4554   {
4555     int door_token;
4556     int part_1, part_8;
4557     struct DoorInfo *door;
4558   }
4559   doors[] =
4560   {
4561     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
4562     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
4563
4564     { -1,       -1,                     -1,                     NULL    }
4565   };
4566   struct Rect door_rect_list[] =
4567   {
4568     { DX, DY, DXSIZE, DYSIZE },
4569     { VX, VY, VXSIZE, VYSIZE }
4570   };
4571   int i, j;
4572
4573   for (i = 0; doors[i].door_token != -1; i++)
4574   {
4575     int door_token = doors[i].door_token;
4576     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4577     int part_1 = doors[i].part_1;
4578     int part_8 = doors[i].part_8;
4579     int part_2 = part_1 + 1;
4580     int part_3 = part_1 + 2;
4581     struct DoorInfo *door = doors[i].door;
4582     struct Rect *door_rect = &door_rect_list[door_index];
4583     boolean door_gfx_redefined = FALSE;
4584
4585     /* check if any door part graphic definitions have been redefined */
4586
4587     for (j = 0; door_part_controls[j].door_token != -1; j++)
4588     {
4589       struct DoorPartControlInfo *dpc = &door_part_controls[j];
4590       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4591
4592       if (dpc->door_token == door_token && fi->redefined)
4593         door_gfx_redefined = TRUE;
4594     }
4595
4596     /* check for old-style door graphic/animation modifications */
4597
4598     if (!door_gfx_redefined)
4599     {
4600       if (door->anim_mode & ANIM_STATIC_PANEL)
4601       {
4602         door->panel.step_xoffset = 0;
4603         door->panel.step_yoffset = 0;
4604       }
4605
4606       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4607       {
4608         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4609         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4610         int num_door_steps, num_panel_steps;
4611
4612         /* remove door part graphics other than the two default wings */
4613
4614         for (j = 0; door_part_controls[j].door_token != -1; j++)
4615         {
4616           struct DoorPartControlInfo *dpc = &door_part_controls[j];
4617           struct GraphicInfo *g = &graphic_info[dpc->graphic];
4618
4619           if (dpc->graphic >= part_3 &&
4620               dpc->graphic <= part_8)
4621             g->bitmap = NULL;
4622         }
4623
4624         /* set graphics and screen positions of the default wings */
4625
4626         g_part_1->width  = door_rect->width;
4627         g_part_1->height = door_rect->height;
4628         g_part_2->width  = door_rect->width;
4629         g_part_2->height = door_rect->height;
4630         g_part_2->src_x = door_rect->width;
4631         g_part_2->src_y = g_part_1->src_y;
4632
4633         door->part_2.x = door->part_1.x;
4634         door->part_2.y = door->part_1.y;
4635
4636         if (door->width != -1)
4637         {
4638           g_part_1->width = door->width;
4639           g_part_2->width = door->width;
4640
4641           // special treatment for graphics and screen position of right wing
4642           g_part_2->src_x += door_rect->width - door->width;
4643           door->part_2.x  += door_rect->width - door->width;
4644         }
4645
4646         if (door->height != -1)
4647         {
4648           g_part_1->height = door->height;
4649           g_part_2->height = door->height;
4650
4651           // special treatment for graphics and screen position of bottom wing
4652           g_part_2->src_y += door_rect->height - door->height;
4653           door->part_2.y  += door_rect->height - door->height;
4654         }
4655
4656         /* set animation delays for the default wings and panels */
4657
4658         door->part_1.step_delay = door->step_delay;
4659         door->part_2.step_delay = door->step_delay;
4660         door->panel.step_delay  = door->step_delay;
4661
4662         /* set animation draw order for the default wings */
4663
4664         door->part_1.sort_priority = 2; /* draw left wing over ... */
4665         door->part_2.sort_priority = 1; /*          ... right wing */
4666
4667         /* set animation draw offset for the default wings */
4668
4669         if (door->anim_mode & ANIM_HORIZONTAL)
4670         {
4671           door->part_1.step_xoffset = door->step_offset;
4672           door->part_1.step_yoffset = 0;
4673           door->part_2.step_xoffset = door->step_offset * -1;
4674           door->part_2.step_yoffset = 0;
4675
4676           num_door_steps = g_part_1->width / door->step_offset;
4677         }
4678         else    // ANIM_VERTICAL
4679         {
4680           door->part_1.step_xoffset = 0;
4681           door->part_1.step_yoffset = door->step_offset;
4682           door->part_2.step_xoffset = 0;
4683           door->part_2.step_yoffset = door->step_offset * -1;
4684
4685           num_door_steps = g_part_1->height / door->step_offset;
4686         }
4687
4688         /* set animation draw offset for the default panels */
4689
4690         if (door->step_offset > 1)
4691         {
4692           num_panel_steps = 2 * door_rect->height / door->step_offset;
4693           door->panel.start_step = num_panel_steps - num_door_steps;
4694           door->panel.start_step_closing = door->panel.start_step;
4695         }
4696         else
4697         {
4698           num_panel_steps = door_rect->height / door->step_offset;
4699           door->panel.start_step = num_panel_steps - num_door_steps / 2;
4700           door->panel.start_step_closing = door->panel.start_step;
4701           door->panel.step_delay *= 2;
4702         }
4703       }
4704     }
4705   }
4706 }
4707
4708 void InitDoors()
4709 {
4710   int i;
4711
4712   for (i = 0; door_part_controls[i].door_token != -1; i++)
4713   {
4714     struct DoorPartControlInfo *dpc = &door_part_controls[i];
4715     struct DoorPartOrderInfo *dpo = &door_part_order[i];
4716
4717     /* initialize "start_step_opening" and "start_step_closing", if needed */
4718     if (dpc->pos->start_step_opening == 0 &&
4719         dpc->pos->start_step_closing == 0)
4720     {
4721       // dpc->pos->start_step_opening = dpc->pos->start_step;
4722       dpc->pos->start_step_closing = dpc->pos->start_step;
4723     }
4724
4725     /* fill structure for door part draw order (sorted below) */
4726     dpo->nr = i;
4727     dpo->sort_priority = dpc->pos->sort_priority;
4728   }
4729
4730   /* sort door part controls according to sort_priority and graphic number */
4731   qsort(door_part_order, MAX_DOOR_PARTS,
4732         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4733 }
4734
4735 unsigned int OpenDoor(unsigned int door_state)
4736 {
4737   if (door_state & DOOR_COPY_BACK)
4738   {
4739     if (door_state & DOOR_OPEN_1)
4740       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4741                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4742
4743     if (door_state & DOOR_OPEN_2)
4744       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4745                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4746
4747     door_state &= ~DOOR_COPY_BACK;
4748   }
4749
4750   return MoveDoor(door_state);
4751 }
4752
4753 unsigned int CloseDoor(unsigned int door_state)
4754 {
4755   unsigned int old_door_state = GetDoorState();
4756
4757   if (!(door_state & DOOR_NO_COPY_BACK))
4758   {
4759     if (old_door_state & DOOR_OPEN_1)
4760       BlitBitmap(backbuffer, bitmap_db_door_1,
4761                  DX, DY, DXSIZE, DYSIZE, 0, 0);
4762
4763     if (old_door_state & DOOR_OPEN_2)
4764       BlitBitmap(backbuffer, bitmap_db_door_2,
4765                  VX, VY, VXSIZE, VYSIZE, 0, 0);
4766
4767     door_state &= ~DOOR_NO_COPY_BACK;
4768   }
4769
4770   return MoveDoor(door_state);
4771 }
4772
4773 unsigned int GetDoorState()
4774 {
4775   return MoveDoor(DOOR_GET_STATE);
4776 }
4777
4778 unsigned int SetDoorState(unsigned int door_state)
4779 {
4780   return MoveDoor(door_state | DOOR_SET_STATE);
4781 }
4782
4783 int euclid(int a, int b)
4784 {
4785   return (b ? euclid(b, a % b) : a);
4786 }
4787
4788 unsigned int MoveDoor(unsigned int door_state)
4789 {
4790   struct Rect door_rect_list[] =
4791   {
4792     { DX, DY, DXSIZE, DYSIZE },
4793     { VX, VY, VXSIZE, VYSIZE }
4794   };
4795   static int door1 = DOOR_CLOSE_1;
4796   static int door2 = DOOR_CLOSE_2;
4797   unsigned int door_delay = 0;
4798   unsigned int door_delay_value;
4799   int i;
4800
4801   if (door_state == DOOR_GET_STATE)
4802     return (door1 | door2);
4803
4804   if (door_state & DOOR_SET_STATE)
4805   {
4806     if (door_state & DOOR_ACTION_1)
4807       door1 = door_state & DOOR_ACTION_1;
4808     if (door_state & DOOR_ACTION_2)
4809       door2 = door_state & DOOR_ACTION_2;
4810
4811     return (door1 | door2);
4812   }
4813
4814   if (!(door_state & DOOR_FORCE_REDRAW))
4815   {
4816     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4817       door_state &= ~DOOR_OPEN_1;
4818     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4819       door_state &= ~DOOR_CLOSE_1;
4820     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4821       door_state &= ~DOOR_OPEN_2;
4822     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4823       door_state &= ~DOOR_CLOSE_2;
4824   }
4825
4826   if (global.autoplay_leveldir)
4827   {
4828     door_state |= DOOR_NO_DELAY;
4829     door_state &= ~DOOR_CLOSE_ALL;
4830   }
4831
4832   if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
4833     door_state |= DOOR_NO_DELAY;
4834
4835   if (door_state & DOOR_ACTION)
4836   {
4837     boolean door_panel_drawn[NUM_DOORS];
4838     boolean panel_has_doors[NUM_DOORS];
4839     boolean door_part_skip[MAX_DOOR_PARTS];
4840     boolean door_part_done[MAX_DOOR_PARTS];
4841     boolean door_part_done_all;
4842     int num_steps[MAX_DOOR_PARTS];
4843     int max_move_delay = 0;     // delay for complete animations of all doors
4844     int max_step_delay = 0;     // delay (ms) between two animation frames
4845     int num_move_steps = 0;     // number of animation steps for all doors
4846     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
4847     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
4848     int current_move_delay = 0;
4849     int start = 0;
4850     int k;
4851
4852     for (i = 0; i < NUM_DOORS; i++)
4853       panel_has_doors[i] = FALSE;
4854
4855     for (i = 0; i < MAX_DOOR_PARTS; i++)
4856     {
4857       struct DoorPartControlInfo *dpc = &door_part_controls[i];
4858       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4859       int door_token = dpc->door_token;
4860
4861       door_part_done[i] = FALSE;
4862       door_part_skip[i] = (!(door_state & door_token) ||
4863                            !g->bitmap);
4864     }
4865
4866     for (i = 0; i < MAX_DOOR_PARTS; i++)
4867     {
4868       int nr = door_part_order[i].nr;
4869       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4870       struct DoorPartPosInfo *pos = dpc->pos;
4871       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4872       int door_token = dpc->door_token;
4873       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4874       boolean is_panel = DOOR_PART_IS_PANEL(nr);
4875       int step_xoffset = ABS(pos->step_xoffset);
4876       int step_yoffset = ABS(pos->step_yoffset);
4877       int step_delay = pos->step_delay;
4878       int current_door_state = door_state & door_token;
4879       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4880       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4881       boolean part_opening = (is_panel ? door_closing : door_opening);
4882       int start_step = (part_opening ? pos->start_step_opening :
4883                         pos->start_step_closing);
4884       float move_xsize = (step_xoffset ? g->width  : 0);
4885       float move_ysize = (step_yoffset ? g->height : 0);
4886       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4887       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4888       int move_steps = (move_xsteps && move_ysteps ?
4889                         MIN(move_xsteps, move_ysteps) :
4890                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
4891       int move_delay = move_steps * step_delay;
4892
4893       if (door_part_skip[nr])
4894         continue;
4895
4896       max_move_delay = MAX(max_move_delay, move_delay);
4897       max_step_delay = (max_step_delay == 0 ? step_delay :
4898                         euclid(max_step_delay, step_delay));
4899       num_steps[nr] = move_steps;
4900
4901       if (!is_panel)
4902       {
4903         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4904
4905         panel_has_doors[door_index] = TRUE;
4906       }
4907     }
4908
4909     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
4910
4911     num_move_steps = max_move_delay / max_step_delay;
4912     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4913
4914     door_delay_value = max_step_delay;
4915
4916     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4917     {
4918       start = num_move_steps - 1;
4919     }
4920     else
4921     {
4922       /* opening door sound has priority over simultaneously closing door */
4923       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4924       {
4925         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4926
4927         if (door_state & DOOR_OPEN_1)
4928           PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
4929         if (door_state & DOOR_OPEN_2)
4930           PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
4931       }
4932       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4933       {
4934         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4935
4936         if (door_state & DOOR_CLOSE_1)
4937           PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
4938         if (door_state & DOOR_CLOSE_2)
4939           PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
4940       }
4941     }
4942
4943     for (k = start; k < num_move_steps; k++)
4944     {
4945       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
4946
4947       door_part_done_all = TRUE;
4948
4949       for (i = 0; i < NUM_DOORS; i++)
4950         door_panel_drawn[i] = FALSE;
4951
4952       for (i = 0; i < MAX_DOOR_PARTS; i++)
4953       {
4954         int nr = door_part_order[i].nr;
4955         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4956         struct DoorPartPosInfo *pos = dpc->pos;
4957         struct GraphicInfo *g = &graphic_info[dpc->graphic];
4958         int door_token = dpc->door_token;
4959         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4960         boolean is_panel = DOOR_PART_IS_PANEL(nr);
4961         boolean is_panel_and_door_has_closed = FALSE;
4962         struct Rect *door_rect = &door_rect_list[door_index];
4963         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4964                                   bitmap_db_door_2);
4965         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4966         int current_door_state = door_state & door_token;
4967         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4968         boolean door_closing = !door_opening;
4969         boolean part_opening = (is_panel ? door_closing : door_opening);
4970         boolean part_closing = !part_opening;
4971         int start_step = (part_opening ? pos->start_step_opening :
4972                           pos->start_step_closing);
4973         int step_delay = pos->step_delay;
4974         int step_factor = step_delay / max_step_delay;
4975         int k1 = (step_factor ? k / step_factor + 1 : k);
4976         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4977         int kk = MAX(0, k2);
4978         int g_src_x = 0;
4979         int g_src_y = 0;
4980         int src_x, src_y, src_xx, src_yy;
4981         int dst_x, dst_y, dst_xx, dst_yy;
4982         int width, height;
4983
4984         if (door_part_skip[nr])
4985           continue;
4986
4987         if (!(door_state & door_token))
4988           continue;
4989
4990         if (!g->bitmap)
4991           continue;
4992
4993         if (!is_panel)
4994         {
4995           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4996           int kk_door = MAX(0, k2_door);
4997           int sync_frame = kk_door * door_delay_value;
4998           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4999
5000           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5001                                 &g_src_x, &g_src_y);
5002         }
5003
5004         // draw door panel
5005
5006         if (!door_panel_drawn[door_index])
5007         {
5008           ClearRectangle(drawto, door_rect->x, door_rect->y,
5009                          door_rect->width, door_rect->height);
5010
5011           door_panel_drawn[door_index] = TRUE;
5012         }
5013
5014         // draw opening or closing door parts
5015
5016         if (pos->step_xoffset < 0)      // door part on right side
5017         {
5018           src_xx = 0;
5019           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5020           width = g->width;
5021
5022           if (dst_xx + width > door_rect->width)
5023             width = door_rect->width - dst_xx;
5024         }
5025         else                            // door part on left side
5026         {
5027           src_xx = 0;
5028           dst_xx = pos->x - kk * pos->step_xoffset;
5029
5030           if (dst_xx < 0)
5031           {
5032             src_xx = ABS(dst_xx);
5033             dst_xx = 0;
5034           }
5035
5036           width = g->width - src_xx;
5037
5038           if (width > door_rect->width)
5039             width = door_rect->width;
5040
5041           // printf("::: k == %d [%d] \n", k, start_step);
5042         }
5043
5044         if (pos->step_yoffset < 0)      // door part on bottom side
5045         {
5046           src_yy = 0;
5047           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5048           height = g->height;
5049
5050           if (dst_yy + height > door_rect->height)
5051             height = door_rect->height - dst_yy;
5052         }
5053         else                            // door part on top side
5054         {
5055           src_yy = 0;
5056           dst_yy = pos->y - kk * pos->step_yoffset;
5057
5058           if (dst_yy < 0)
5059           {
5060             src_yy = ABS(dst_yy);
5061             dst_yy = 0;
5062           }
5063
5064           height = g->height - src_yy;
5065         }
5066
5067         src_x = g_src_x + src_xx;
5068         src_y = g_src_y + src_yy;
5069
5070         dst_x = door_rect->x + dst_xx;
5071         dst_y = door_rect->y + dst_yy;
5072
5073         is_panel_and_door_has_closed =
5074           (is_panel &&
5075            door_closing &&
5076            panel_has_doors[door_index] &&
5077            k >= num_move_steps_doors_only - 1);
5078
5079         if (width  >= 0 && width  <= g->width &&
5080             height >= 0 && height <= g->height &&
5081             !is_panel_and_door_has_closed)
5082         {
5083           if (is_panel || !pos->draw_masked)
5084             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5085                        dst_x, dst_y);
5086           else
5087             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5088                              dst_x, dst_y);
5089         }
5090
5091         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5092
5093         if ((part_opening && (width < 0         || height < 0)) ||
5094             (part_closing && (width >= g->width && height >= g->height)))
5095           door_part_done[nr] = TRUE;
5096
5097         // continue door part animations, but not panel after door has closed
5098         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5099           door_part_done_all = FALSE;
5100       }
5101
5102       if (!(door_state & DOOR_NO_DELAY))
5103       {
5104         BackToFront();
5105
5106         SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5107
5108         current_move_delay += max_step_delay;
5109
5110         /* prevent OS (Windows) from complaining about program not responding */
5111         CheckQuitEvent();
5112       }
5113
5114       if (door_part_done_all)
5115         break;
5116     }
5117
5118     if (!(door_state & DOOR_NO_DELAY))
5119     {
5120       /* wait for specified door action post delay */
5121       if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5122         Delay(MAX(door_1.post_delay, door_2.post_delay));
5123       else if (door_state & DOOR_ACTION_1)
5124         Delay(door_1.post_delay);
5125       else if (door_state & DOOR_ACTION_2)
5126         Delay(door_2.post_delay);
5127     }
5128   }
5129
5130   if (door_state & DOOR_ACTION_1)
5131     door1 = door_state & DOOR_ACTION_1;
5132   if (door_state & DOOR_ACTION_2)
5133     door2 = door_state & DOOR_ACTION_2;
5134
5135   // draw masked border over door area
5136   DrawMaskedBorder(REDRAW_DOOR_1);
5137   DrawMaskedBorder(REDRAW_DOOR_2);
5138
5139   return (door1 | door2);
5140 }
5141
5142 static boolean useSpecialEditorDoor()
5143 {
5144   int graphic = IMG_GLOBAL_BORDER_EDITOR;
5145   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5146
5147   // do not draw special editor door if editor border defined or redefined
5148   if (graphic_info[graphic].bitmap != NULL || redefined)
5149     return FALSE;
5150
5151   // do not draw special editor door if global border defined to be empty
5152   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5153     return FALSE;
5154
5155   // do not draw special editor door if viewport definitions do not match
5156   if (EX != VX ||
5157       EY >= VY ||
5158       EXSIZE != VXSIZE ||
5159       EY + EYSIZE != VY + VYSIZE)
5160     return FALSE;
5161
5162   return TRUE;
5163 }
5164
5165 void DrawSpecialEditorDoor()
5166 {
5167   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5168   int top_border_width = gfx1->width;
5169   int top_border_height = gfx1->height;
5170   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5171   int ex = EX - outer_border;
5172   int ey = EY - outer_border;
5173   int vy = VY - outer_border;
5174   int exsize = EXSIZE + 2 * outer_border;
5175
5176   if (!useSpecialEditorDoor())
5177     return;
5178
5179   /* draw bigger level editor toolbox window */
5180   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5181              top_border_width, top_border_height, ex, ey - top_border_height);
5182   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5183              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5184
5185   redraw_mask |= REDRAW_ALL;
5186 }
5187
5188 void UndrawSpecialEditorDoor()
5189 {
5190   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5191   int top_border_width = gfx1->width;
5192   int top_border_height = gfx1->height;
5193   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5194   int ex = EX - outer_border;
5195   int ey = EY - outer_border;
5196   int ey_top = ey - top_border_height;
5197   int exsize = EXSIZE + 2 * outer_border;
5198   int eysize = EYSIZE + 2 * outer_border;
5199
5200   if (!useSpecialEditorDoor())
5201     return;
5202
5203   /* draw normal tape recorder window */
5204   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5205   {
5206     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5207                ex, ey_top, top_border_width, top_border_height,
5208                ex, ey_top);
5209     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5210                ex, ey, exsize, eysize, ex, ey);
5211   }
5212   else
5213   {
5214     // if screen background is set to "[NONE]", clear editor toolbox window
5215     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5216     ClearRectangle(drawto, ex, ey, exsize, eysize);
5217   }
5218
5219   redraw_mask |= REDRAW_ALL;
5220 }
5221
5222
5223 /* ---------- new tool button stuff ---------------------------------------- */
5224
5225 static struct
5226 {
5227   int graphic;
5228   struct TextPosInfo *pos;
5229   int gadget_id;
5230   char *infotext;
5231 } toolbutton_info[NUM_TOOL_BUTTONS] =
5232 {
5233   {
5234     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
5235     TOOL_CTRL_ID_YES,                   "yes"
5236   },
5237   {
5238     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5239     TOOL_CTRL_ID_NO,                    "no"
5240   },
5241   {
5242     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5243     TOOL_CTRL_ID_CONFIRM,               "confirm"
5244   },
5245   {
5246     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5247     TOOL_CTRL_ID_PLAYER_1,              "player 1"
5248   },
5249   {
5250     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5251     TOOL_CTRL_ID_PLAYER_2,              "player 2"
5252   },
5253   {
5254     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5255     TOOL_CTRL_ID_PLAYER_3,              "player 3"
5256   },
5257   {
5258     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5259     TOOL_CTRL_ID_PLAYER_4,              "player 4"
5260   }
5261 };
5262
5263 void CreateToolButtons()
5264 {
5265   int i;
5266
5267   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5268   {
5269     struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5270     struct TextPosInfo *pos = toolbutton_info[i].pos;
5271     struct GadgetInfo *gi;
5272     Bitmap *deco_bitmap = None;
5273     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5274     unsigned int event_mask = GD_EVENT_RELEASED;
5275     int dx = DX;
5276     int dy = DY;
5277     int gd_x = gfx->src_x;
5278     int gd_y = gfx->src_y;
5279     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5280     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5281     int id = i;
5282
5283     if (global.use_envelope_request)
5284       setRequestPosition(&dx, &dy, TRUE);
5285
5286     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5287     {
5288       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5289
5290       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5291                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5292       deco_xpos = (gfx->width  - pos->size) / 2;
5293       deco_ypos = (gfx->height - pos->size) / 2;
5294     }
5295
5296     gi = CreateGadget(GDI_CUSTOM_ID, id,
5297                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5298                       GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5299                       GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5300                       GDI_WIDTH, gfx->width,
5301                       GDI_HEIGHT, gfx->height,
5302                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5303                       GDI_STATE, GD_BUTTON_UNPRESSED,
5304                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5305                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5306                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5307                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5308                       GDI_DECORATION_SIZE, pos->size, pos->size,
5309                       GDI_DECORATION_SHIFTING, 1, 1,
5310                       GDI_DIRECT_DRAW, FALSE,
5311                       GDI_EVENT_MASK, event_mask,
5312                       GDI_CALLBACK_ACTION, HandleToolButtons,
5313                       GDI_END);
5314
5315     if (gi == NULL)
5316       Error(ERR_EXIT, "cannot create gadget");
5317
5318     tool_gadget[id] = gi;
5319   }
5320 }
5321
5322 void FreeToolButtons()
5323 {
5324   int i;
5325
5326   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5327     FreeGadget(tool_gadget[i]);
5328 }
5329
5330 static void UnmapToolButtons()
5331 {
5332   int i;
5333
5334   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5335     UnmapGadget(tool_gadget[i]);
5336 }
5337
5338 static void HandleToolButtons(struct GadgetInfo *gi)
5339 {
5340   request_gadget_id = gi->custom_id;
5341 }
5342
5343 static struct Mapping_EM_to_RND_object
5344 {
5345   int element_em;
5346   boolean is_rnd_to_em_mapping;         /* unique mapping EM <-> RND */
5347   boolean is_backside;                  /* backside of moving element */
5348
5349   int element_rnd;
5350   int action;
5351   int direction;
5352 }
5353 em_object_mapping_list[] =
5354 {
5355   {
5356     Xblank,                             TRUE,   FALSE,
5357     EL_EMPTY,                           -1, -1
5358   },
5359   {
5360     Yacid_splash_eB,                    FALSE,  FALSE,
5361     EL_ACID_SPLASH_RIGHT,               -1, -1
5362   },
5363   {
5364     Yacid_splash_wB,                    FALSE,  FALSE,
5365     EL_ACID_SPLASH_LEFT,                -1, -1
5366   },
5367
5368 #ifdef EM_ENGINE_BAD_ROLL
5369   {
5370     Xstone_force_e,                     FALSE,  FALSE,
5371     EL_ROCK,                            -1, MV_BIT_RIGHT
5372   },
5373   {
5374     Xstone_force_w,                     FALSE,  FALSE,
5375     EL_ROCK,                            -1, MV_BIT_LEFT
5376   },
5377   {
5378     Xnut_force_e,                       FALSE,  FALSE,
5379     EL_NUT,                             -1, MV_BIT_RIGHT
5380   },
5381   {
5382     Xnut_force_w,                       FALSE,  FALSE,
5383     EL_NUT,                             -1, MV_BIT_LEFT
5384   },
5385   {
5386     Xspring_force_e,                    FALSE,  FALSE,
5387     EL_SPRING,                          -1, MV_BIT_RIGHT
5388   },
5389   {
5390     Xspring_force_w,                    FALSE,  FALSE,
5391     EL_SPRING,                          -1, MV_BIT_LEFT
5392   },
5393   {
5394     Xemerald_force_e,                   FALSE,  FALSE,
5395     EL_EMERALD,                         -1, MV_BIT_RIGHT
5396   },
5397   {
5398     Xemerald_force_w,                   FALSE,  FALSE,
5399     EL_EMERALD,                         -1, MV_BIT_LEFT
5400   },
5401   {
5402     Xdiamond_force_e,                   FALSE,  FALSE,
5403     EL_DIAMOND,                         -1, MV_BIT_RIGHT
5404   },
5405   {
5406     Xdiamond_force_w,                   FALSE,  FALSE,
5407     EL_DIAMOND,                         -1, MV_BIT_LEFT
5408   },
5409   {
5410     Xbomb_force_e,                      FALSE,  FALSE,
5411     EL_BOMB,                            -1, MV_BIT_RIGHT
5412   },
5413   {
5414     Xbomb_force_w,                      FALSE,  FALSE,
5415     EL_BOMB,                            -1, MV_BIT_LEFT
5416   },
5417 #endif  /* EM_ENGINE_BAD_ROLL */
5418
5419   {
5420     Xstone,                             TRUE,   FALSE,
5421     EL_ROCK,                            -1, -1
5422   },
5423   {
5424     Xstone_pause,                       FALSE,  FALSE,
5425     EL_ROCK,                            -1, -1
5426   },
5427   {
5428     Xstone_fall,                        FALSE,  FALSE,
5429     EL_ROCK,                            -1, -1
5430   },
5431   {
5432     Ystone_s,                           FALSE,  FALSE,
5433     EL_ROCK,                            ACTION_FALLING, -1
5434   },
5435   {
5436     Ystone_sB,                          FALSE,  TRUE,
5437     EL_ROCK,                            ACTION_FALLING, -1
5438   },
5439   {
5440     Ystone_e,                           FALSE,  FALSE,
5441     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5442   },
5443   {
5444     Ystone_eB,                          FALSE,  TRUE,
5445     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5446   },
5447   {
5448     Ystone_w,                           FALSE,  FALSE,
5449     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5450   },
5451   {
5452     Ystone_wB,                          FALSE,  TRUE,
5453     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5454   },
5455   {
5456     Xnut,                               TRUE,   FALSE,
5457     EL_NUT,                             -1, -1
5458   },
5459   {
5460     Xnut_pause,                         FALSE,  FALSE,
5461     EL_NUT,                             -1, -1
5462   },
5463   {
5464     Xnut_fall,                          FALSE,  FALSE,
5465     EL_NUT,                             -1, -1
5466   },
5467   {
5468     Ynut_s,                             FALSE,  FALSE,
5469     EL_NUT,                             ACTION_FALLING, -1
5470   },
5471   {
5472     Ynut_sB,                            FALSE,  TRUE,
5473     EL_NUT,                             ACTION_FALLING, -1
5474   },
5475   {
5476     Ynut_e,                             FALSE,  FALSE,
5477     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5478   },
5479   {
5480     Ynut_eB,                            FALSE,  TRUE,
5481     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5482   },
5483   {
5484     Ynut_w,                             FALSE,  FALSE,
5485     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5486   },
5487   {
5488     Ynut_wB,                            FALSE,  TRUE,
5489     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5490   },
5491   {
5492     Xbug_n,                             TRUE,   FALSE,
5493     EL_BUG_UP,                          -1, -1
5494   },
5495   {
5496     Xbug_e,                             TRUE,   FALSE,
5497     EL_BUG_RIGHT,                       -1, -1
5498   },
5499   {
5500     Xbug_s,                             TRUE,   FALSE,
5501     EL_BUG_DOWN,                        -1, -1
5502   },
5503   {
5504     Xbug_w,                             TRUE,   FALSE,
5505     EL_BUG_LEFT,                        -1, -1
5506   },
5507   {
5508     Xbug_gon,                           FALSE,  FALSE,
5509     EL_BUG_UP,                          -1, -1
5510   },
5511   {
5512     Xbug_goe,                           FALSE,  FALSE,
5513     EL_BUG_RIGHT,                       -1, -1
5514   },
5515   {
5516     Xbug_gos,                           FALSE,  FALSE,
5517     EL_BUG_DOWN,                        -1, -1
5518   },
5519   {
5520     Xbug_gow,                           FALSE,  FALSE,
5521     EL_BUG_LEFT,                        -1, -1
5522   },
5523   {
5524     Ybug_n,                             FALSE,  FALSE,
5525     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5526   },
5527   {
5528     Ybug_nB,                            FALSE,  TRUE,
5529     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5530   },
5531   {
5532     Ybug_e,                             FALSE,  FALSE,
5533     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5534   },
5535   {
5536     Ybug_eB,                            FALSE,  TRUE,
5537     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5538   },
5539   {
5540     Ybug_s,                             FALSE,  FALSE,
5541     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5542   },
5543   {
5544     Ybug_sB,                            FALSE,  TRUE,
5545     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5546   },
5547   {
5548     Ybug_w,                             FALSE,  FALSE,
5549     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5550   },
5551   {
5552     Ybug_wB,                            FALSE,  TRUE,
5553     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5554   },
5555   {
5556     Ybug_w_n,                           FALSE,  FALSE,
5557     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5558   },
5559   {
5560     Ybug_n_e,                           FALSE,  FALSE,
5561     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5562   },
5563   {
5564     Ybug_e_s,                           FALSE,  FALSE,
5565     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5566   },
5567   {
5568     Ybug_s_w,                           FALSE,  FALSE,
5569     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5570   },
5571   {
5572     Ybug_e_n,                           FALSE,  FALSE,
5573     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5574   },
5575   {
5576     Ybug_s_e,                           FALSE,  FALSE,
5577     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5578   },
5579   {
5580     Ybug_w_s,                           FALSE,  FALSE,
5581     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5582   },
5583   {
5584     Ybug_n_w,                           FALSE,  FALSE,
5585     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5586   },
5587   {
5588     Ybug_stone,                         FALSE,  FALSE,
5589     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
5590   },
5591   {
5592     Ybug_spring,                        FALSE,  FALSE,
5593     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
5594   },
5595   {
5596     Xtank_n,                            TRUE,   FALSE,
5597     EL_SPACESHIP_UP,                    -1, -1
5598   },
5599   {
5600     Xtank_e,                            TRUE,   FALSE,
5601     EL_SPACESHIP_RIGHT,                 -1, -1
5602   },
5603   {
5604     Xtank_s,                            TRUE,   FALSE,
5605     EL_SPACESHIP_DOWN,                  -1, -1
5606   },
5607   {
5608     Xtank_w,                            TRUE,   FALSE,
5609     EL_SPACESHIP_LEFT,                  -1, -1
5610   },
5611   {
5612     Xtank_gon,                          FALSE,  FALSE,
5613     EL_SPACESHIP_UP,                    -1, -1
5614   },
5615   {
5616     Xtank_goe,                          FALSE,  FALSE,
5617     EL_SPACESHIP_RIGHT,                 -1, -1
5618   },
5619   {
5620     Xtank_gos,                          FALSE,  FALSE,
5621     EL_SPACESHIP_DOWN,                  -1, -1
5622   },
5623   {
5624     Xtank_gow,                          FALSE,  FALSE,
5625     EL_SPACESHIP_LEFT,                  -1, -1
5626   },
5627   {
5628     Ytank_n,                            FALSE,  FALSE,
5629     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5630   },
5631   {
5632     Ytank_nB,                           FALSE,  TRUE,
5633     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5634   },
5635   {
5636     Ytank_e,                            FALSE,  FALSE,
5637     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5638   },
5639   {
5640     Ytank_eB,                           FALSE,  TRUE,
5641     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5642   },
5643   {
5644     Ytank_s,                            FALSE,  FALSE,
5645     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5646   },
5647   {
5648     Ytank_sB,                           FALSE,  TRUE,
5649     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5650   },
5651   {
5652     Ytank_w,                            FALSE,  FALSE,
5653     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5654   },
5655   {
5656     Ytank_wB,                           FALSE,  TRUE,
5657     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5658   },
5659   {
5660     Ytank_w_n,                          FALSE,  FALSE,
5661     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5662   },
5663   {
5664     Ytank_n_e,                          FALSE,  FALSE,
5665     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5666   },
5667   {
5668     Ytank_e_s,                          FALSE,  FALSE,
5669     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5670   },
5671   {
5672     Ytank_s_w,                          FALSE,  FALSE,
5673     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5674   },
5675   {
5676     Ytank_e_n,                          FALSE,  FALSE,
5677     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5678   },
5679   {
5680     Ytank_s_e,                          FALSE,  FALSE,
5681     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5682   },
5683   {
5684     Ytank_w_s,                          FALSE,  FALSE,
5685     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5686   },
5687   {
5688     Ytank_n_w,                          FALSE,  FALSE,
5689     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5690   },
5691   {
5692     Ytank_stone,                        FALSE,  FALSE,
5693     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
5694   },
5695   {
5696     Ytank_spring,                       FALSE,  FALSE,
5697     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
5698   },
5699   {
5700     Xandroid,                           TRUE,   FALSE,
5701     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
5702   },
5703   {
5704     Xandroid_1_n,                       FALSE,  FALSE,
5705     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5706   },
5707   {
5708     Xandroid_2_n,                       FALSE,  FALSE,
5709     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5710   },
5711   {
5712     Xandroid_1_e,                       FALSE,  FALSE,
5713     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5714   },
5715   {
5716     Xandroid_2_e,                       FALSE,  FALSE,
5717     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5718   },
5719   {
5720     Xandroid_1_w,                       FALSE,  FALSE,
5721     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5722   },
5723   {
5724     Xandroid_2_w,                       FALSE,  FALSE,
5725     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5726   },
5727   {
5728     Xandroid_1_s,                       FALSE,  FALSE,
5729     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5730   },
5731   {
5732     Xandroid_2_s,                       FALSE,  FALSE,
5733     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5734   },
5735   {
5736     Yandroid_n,                         FALSE,  FALSE,
5737     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5738   },
5739   {
5740     Yandroid_nB,                        FALSE,  TRUE,
5741     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5742   },
5743   {
5744     Yandroid_ne,                        FALSE,  FALSE,
5745     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
5746   },
5747   {
5748     Yandroid_neB,                       FALSE,  TRUE,
5749     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
5750   },
5751   {
5752     Yandroid_e,                         FALSE,  FALSE,
5753     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5754   },
5755   {
5756     Yandroid_eB,                        FALSE,  TRUE,
5757     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5758   },
5759   {
5760     Yandroid_se,                        FALSE,  FALSE,
5761     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
5762   },
5763   {
5764     Yandroid_seB,                       FALSE,  TRUE,
5765     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5766   },
5767   {
5768     Yandroid_s,                         FALSE,  FALSE,
5769     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5770   },
5771   {
5772     Yandroid_sB,                        FALSE,  TRUE,
5773     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5774   },
5775   {
5776     Yandroid_sw,                        FALSE,  FALSE,
5777     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
5778   },
5779   {
5780     Yandroid_swB,                       FALSE,  TRUE,
5781     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
5782   },
5783   {
5784     Yandroid_w,                         FALSE,  FALSE,
5785     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5786   },
5787   {
5788     Yandroid_wB,                        FALSE,  TRUE,
5789     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5790   },
5791   {
5792     Yandroid_nw,                        FALSE,  FALSE,
5793     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
5794   },
5795   {
5796     Yandroid_nwB,                       FALSE,  TRUE,
5797     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
5798   },
5799   {
5800     Xspring,                            TRUE,   FALSE,
5801     EL_SPRING,                          -1, -1
5802   },
5803   {
5804     Xspring_pause,                      FALSE,  FALSE,
5805     EL_SPRING,                          -1, -1
5806   },
5807   {
5808     Xspring_e,                          FALSE,  FALSE,
5809     EL_SPRING,                          -1, -1
5810   },
5811   {
5812     Xspring_w,                          FALSE,  FALSE,
5813     EL_SPRING,                          -1, -1
5814   },
5815   {
5816     Xspring_fall,                       FALSE,  FALSE,
5817     EL_SPRING,                          -1, -1
5818   },
5819   {
5820     Yspring_s,                          FALSE,  FALSE,
5821     EL_SPRING,                          ACTION_FALLING, -1
5822   },
5823   {
5824     Yspring_sB,                         FALSE,  TRUE,
5825     EL_SPRING,                          ACTION_FALLING, -1
5826   },
5827   {
5828     Yspring_e,                          FALSE,  FALSE,
5829     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5830   },
5831   {
5832     Yspring_eB,                         FALSE,  TRUE,
5833     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5834   },
5835   {
5836     Yspring_w,                          FALSE,  FALSE,
5837     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5838   },
5839   {
5840     Yspring_wB,                         FALSE,  TRUE,
5841     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5842   },
5843   {
5844     Yspring_kill_e,                     FALSE,  FALSE,
5845     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5846   },
5847   {
5848     Yspring_kill_eB,                    FALSE,  TRUE,
5849     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5850   },
5851   {
5852     Yspring_kill_w,                     FALSE,  FALSE,
5853     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5854   },
5855   {
5856     Yspring_kill_wB,                    FALSE,  TRUE,
5857     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5858   },
5859   {
5860     Xeater_n,                           TRUE,   FALSE,
5861     EL_YAMYAM_UP,                       -1, -1
5862   },
5863   {
5864     Xeater_e,                           TRUE,   FALSE,
5865     EL_YAMYAM_RIGHT,                    -1, -1
5866   },
5867   {
5868     Xeater_w,                           TRUE,   FALSE,
5869     EL_YAMYAM_LEFT,                     -1, -1
5870   },
5871   {
5872     Xeater_s,                           TRUE,   FALSE,
5873     EL_YAMYAM_DOWN,                     -1, -1
5874   },
5875   {
5876     Yeater_n,                           FALSE,  FALSE,
5877     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5878   },
5879   {
5880     Yeater_nB,                          FALSE,  TRUE,
5881     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5882   },
5883   {
5884     Yeater_e,                           FALSE,  FALSE,
5885     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5886   },
5887   {
5888     Yeater_eB,                          FALSE,  TRUE,
5889     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5890   },
5891   {
5892     Yeater_s,                           FALSE,  FALSE,
5893     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5894   },
5895   {
5896     Yeater_sB,                          FALSE,  TRUE,
5897     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5898   },
5899   {
5900     Yeater_w,                           FALSE,  FALSE,
5901     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5902   },
5903   {
5904     Yeater_wB,                          FALSE,  TRUE,
5905     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5906   },
5907   {
5908     Yeater_stone,                       FALSE,  FALSE,
5909     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
5910   },
5911   {
5912     Yeater_spring,                      FALSE,  FALSE,
5913     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
5914   },
5915   {
5916     Xalien,                             TRUE,   FALSE,
5917     EL_ROBOT,                           -1, -1
5918   },
5919   {
5920     Xalien_pause,                       FALSE,  FALSE,
5921     EL_ROBOT,                           -1, -1
5922   },
5923   {
5924     Yalien_n,                           FALSE,  FALSE,
5925     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5926   },
5927   {
5928     Yalien_nB,                          FALSE,  TRUE,
5929     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5930   },
5931   {
5932     Yalien_e,                           FALSE,  FALSE,
5933     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5934   },
5935   {
5936     Yalien_eB,                          FALSE,  TRUE,
5937     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5938   },
5939   {
5940     Yalien_s,                           FALSE,  FALSE,
5941     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5942   },
5943   {
5944     Yalien_sB,                          FALSE,  TRUE,
5945     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5946   },
5947   {
5948     Yalien_w,                           FALSE,  FALSE,
5949     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5950   },
5951   {
5952     Yalien_wB,                          FALSE,  TRUE,
5953     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5954   },
5955   {
5956     Yalien_stone,                       FALSE,  FALSE,
5957     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
5958   },
5959   {
5960     Yalien_spring,                      FALSE,  FALSE,
5961     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
5962   },
5963   {
5964     Xemerald,                           TRUE,   FALSE,
5965     EL_EMERALD,                         -1, -1
5966   },
5967   {
5968     Xemerald_pause,                     FALSE,  FALSE,
5969     EL_EMERALD,                         -1, -1
5970   },
5971   {
5972     Xemerald_fall,                      FALSE,  FALSE,
5973     EL_EMERALD,                         -1, -1
5974   },
5975   {
5976     Xemerald_shine,                     FALSE,  FALSE,
5977     EL_EMERALD,                         ACTION_TWINKLING, -1
5978   },
5979   {
5980     Yemerald_s,                         FALSE,  FALSE,
5981     EL_EMERALD,                         ACTION_FALLING, -1
5982   },
5983   {
5984     Yemerald_sB,                        FALSE,  TRUE,
5985     EL_EMERALD,                         ACTION_FALLING, -1
5986   },
5987   {
5988     Yemerald_e,                         FALSE,  FALSE,
5989     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5990   },
5991   {
5992     Yemerald_eB,                        FALSE,  TRUE,
5993     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5994   },
5995   {
5996     Yemerald_w,                         FALSE,  FALSE,
5997     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5998   },
5999   {
6000     Yemerald_wB,                        FALSE,  TRUE,
6001     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6002   },
6003   {
6004     Yemerald_eat,                       FALSE,  FALSE,
6005     EL_EMERALD,                         ACTION_COLLECTING, -1
6006   },
6007   {
6008     Yemerald_stone,                     FALSE,  FALSE,
6009     EL_NUT,                             ACTION_BREAKING, -1
6010   },
6011   {
6012     Xdiamond,                           TRUE,   FALSE,
6013     EL_DIAMOND,                         -1, -1
6014   },
6015   {
6016     Xdiamond_pause,                     FALSE,  FALSE,
6017     EL_DIAMOND,                         -1, -1
6018   },
6019   {
6020     Xdiamond_fall,                      FALSE,  FALSE,
6021     EL_DIAMOND,                         -1, -1
6022   },
6023   {
6024     Xdiamond_shine,                     FALSE,  FALSE,
6025     EL_DIAMOND,                         ACTION_TWINKLING, -1
6026   },
6027   {
6028     Ydiamond_s,                         FALSE,  FALSE,
6029     EL_DIAMOND,                         ACTION_FALLING, -1
6030   },
6031   {
6032     Ydiamond_sB,                        FALSE,  TRUE,
6033     EL_DIAMOND,                         ACTION_FALLING, -1
6034   },
6035   {
6036     Ydiamond_e,                         FALSE,  FALSE,
6037     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6038   },
6039   {
6040     Ydiamond_eB,                        FALSE,  TRUE,
6041     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6042   },
6043   {
6044     Ydiamond_w,                         FALSE,  FALSE,
6045     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6046   },
6047   {
6048     Ydiamond_wB,                        FALSE,  TRUE,
6049     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6050   },
6051   {
6052     Ydiamond_eat,                       FALSE,  FALSE,
6053     EL_DIAMOND,                         ACTION_COLLECTING, -1
6054   },
6055   {
6056     Ydiamond_stone,                     FALSE,  FALSE,
6057     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
6058   },
6059   {
6060     Xdrip_fall,                         TRUE,   FALSE,
6061     EL_AMOEBA_DROP,                     -1, -1
6062   },
6063   {
6064     Xdrip_stretch,                      FALSE,  FALSE,
6065     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6066   },
6067   {
6068     Xdrip_stretchB,                     FALSE,  TRUE,
6069     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6070   },
6071   {
6072     Xdrip_eat,                          FALSE,  FALSE,
6073     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
6074   },
6075   {
6076     Ydrip_s1,                           FALSE,  FALSE,
6077     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6078   },
6079   {
6080     Ydrip_s1B,                          FALSE,  TRUE,
6081     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6082   },
6083   {
6084     Ydrip_s2,                           FALSE,  FALSE,
6085     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6086   },
6087   {
6088     Ydrip_s2B,                          FALSE,  TRUE,
6089     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6090   },
6091   {
6092     Xbomb,                              TRUE,   FALSE,
6093     EL_BOMB,                            -1, -1
6094   },
6095   {
6096     Xbomb_pause,                        FALSE,  FALSE,
6097     EL_BOMB,                            -1, -1
6098   },
6099   {
6100     Xbomb_fall,                         FALSE,  FALSE,
6101     EL_BOMB,                            -1, -1
6102   },
6103   {
6104     Ybomb_s,                            FALSE,  FALSE,
6105     EL_BOMB,                            ACTION_FALLING, -1
6106   },
6107   {
6108     Ybomb_sB,                           FALSE,  TRUE,
6109     EL_BOMB,                            ACTION_FALLING, -1
6110   },
6111   {
6112     Ybomb_e,                            FALSE,  FALSE,
6113     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6114   },
6115   {
6116     Ybomb_eB,                           FALSE,  TRUE,
6117     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6118   },
6119   {
6120     Ybomb_w,                            FALSE,  FALSE,
6121     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6122   },
6123   {
6124     Ybomb_wB,                           FALSE,  TRUE,
6125     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6126   },
6127   {
6128     Ybomb_eat,                          FALSE,  FALSE,
6129     EL_BOMB,                            ACTION_ACTIVATING, -1
6130   },
6131   {
6132     Xballoon,                           TRUE,   FALSE,
6133     EL_BALLOON,                         -1, -1
6134   },
6135   {
6136     Yballoon_n,                         FALSE,  FALSE,
6137     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6138   },
6139   {
6140     Yballoon_nB,                        FALSE,  TRUE,
6141     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6142   },
6143   {
6144     Yballoon_e,                         FALSE,  FALSE,
6145     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6146   },
6147   {
6148     Yballoon_eB,                        FALSE,  TRUE,
6149     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6150   },
6151   {
6152     Yballoon_s,                         FALSE,  FALSE,
6153     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6154   },
6155   {
6156     Yballoon_sB,                        FALSE,  TRUE,
6157     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6158   },
6159   {
6160     Yballoon_w,                         FALSE,  FALSE,
6161     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6162   },
6163   {
6164     Yballoon_wB,                        FALSE,  TRUE,
6165     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6166   },
6167   {
6168     Xgrass,                             TRUE,   FALSE,
6169     EL_EMC_GRASS,                       -1, -1
6170   },
6171   {
6172     Ygrass_nB,                          FALSE,  FALSE,
6173     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
6174   },
6175   {
6176     Ygrass_eB,                          FALSE,  FALSE,
6177     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
6178   },
6179   {
6180     Ygrass_sB,                          FALSE,  FALSE,
6181     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
6182   },
6183   {
6184     Ygrass_wB,                          FALSE,  FALSE,
6185     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
6186   },
6187   {
6188     Xdirt,                              TRUE,   FALSE,
6189     EL_SAND,                            -1, -1
6190   },
6191   {
6192     Ydirt_nB,                           FALSE,  FALSE,
6193     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
6194   },
6195   {
6196     Ydirt_eB,                           FALSE,  FALSE,
6197     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
6198   },
6199   {
6200     Ydirt_sB,                           FALSE,  FALSE,
6201     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
6202   },
6203   {
6204     Ydirt_wB,                           FALSE,  FALSE,
6205     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
6206   },
6207   {
6208     Xacid_ne,                           TRUE,   FALSE,
6209     EL_ACID_POOL_TOPRIGHT,              -1, -1
6210   },
6211   {
6212     Xacid_se,                           TRUE,   FALSE,
6213     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
6214   },
6215   {
6216     Xacid_s,                            TRUE,   FALSE,
6217     EL_ACID_POOL_BOTTOM,                -1, -1
6218   },
6219   {
6220     Xacid_sw,                           TRUE,   FALSE,
6221     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
6222   },
6223   {
6224     Xacid_nw,                           TRUE,   FALSE,
6225     EL_ACID_POOL_TOPLEFT,               -1, -1
6226   },
6227   {
6228     Xacid_1,                            TRUE,   FALSE,
6229     EL_ACID,                            -1, -1
6230   },
6231   {
6232     Xacid_2,                            FALSE,  FALSE,
6233     EL_ACID,                            -1, -1
6234   },
6235   {
6236     Xacid_3,                            FALSE,  FALSE,
6237     EL_ACID,                            -1, -1
6238   },
6239   {
6240     Xacid_4,                            FALSE,  FALSE,
6241     EL_ACID,                            -1, -1
6242   },
6243   {
6244     Xacid_5,                            FALSE,  FALSE,
6245     EL_ACID,                            -1, -1
6246   },
6247   {
6248     Xacid_6,                            FALSE,  FALSE,
6249     EL_ACID,                            -1, -1
6250   },
6251   {
6252     Xacid_7,                            FALSE,  FALSE,
6253     EL_ACID,                            -1, -1
6254   },
6255   {
6256     Xacid_8,                            FALSE,  FALSE,
6257     EL_ACID,                            -1, -1
6258   },
6259   {
6260     Xball_1,                            TRUE,   FALSE,
6261     EL_EMC_MAGIC_BALL,                  -1, -1
6262   },
6263   {
6264     Xball_1B,                           FALSE,  FALSE,
6265     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6266   },
6267   {
6268     Xball_2,                            FALSE,  FALSE,
6269     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6270   },
6271   {
6272     Xball_2B,                           FALSE,  FALSE,
6273     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6274   },
6275   {
6276     Yball_eat,                          FALSE,  FALSE,
6277     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
6278   },
6279   {
6280     Ykey_1_eat,                         FALSE,  FALSE,
6281     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
6282   },
6283   {
6284     Ykey_2_eat,                         FALSE,  FALSE,
6285     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
6286   },
6287   {
6288     Ykey_3_eat,                         FALSE,  FALSE,
6289     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
6290   },
6291   {
6292     Ykey_4_eat,                         FALSE,  FALSE,
6293     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
6294   },
6295   {
6296     Ykey_5_eat,                         FALSE,  FALSE,
6297     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
6298   },
6299   {
6300     Ykey_6_eat,                         FALSE,  FALSE,
6301     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
6302   },
6303   {
6304     Ykey_7_eat,                         FALSE,  FALSE,
6305     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
6306   },
6307   {
6308     Ykey_8_eat,                         FALSE,  FALSE,
6309     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
6310   },
6311   {
6312     Ylenses_eat,                        FALSE,  FALSE,
6313     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
6314   },
6315   {
6316     Ymagnify_eat,                       FALSE,  FALSE,
6317     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
6318   },
6319   {
6320     Ygrass_eat,                         FALSE,  FALSE,
6321     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
6322   },
6323   {
6324     Ydirt_eat,                          FALSE,  FALSE,
6325     EL_SAND,                            ACTION_SNAPPING, -1
6326   },
6327   {
6328     Xgrow_ns,                           TRUE,   FALSE,
6329     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
6330   },
6331   {
6332     Ygrow_ns_eat,                       FALSE,  FALSE,
6333     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
6334   },
6335   {
6336     Xgrow_ew,                           TRUE,   FALSE,
6337     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
6338   },
6339   {
6340     Ygrow_ew_eat,                       FALSE,  FALSE,
6341     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
6342   },
6343   {
6344     Xwonderwall,                        TRUE,   FALSE,
6345     EL_MAGIC_WALL,                      -1, -1
6346   },
6347   {
6348     XwonderwallB,                       FALSE,  FALSE,
6349     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
6350   },
6351   {
6352     Xamoeba_1,                          TRUE,   FALSE,
6353     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6354   },
6355   {
6356     Xamoeba_2,                          FALSE,  FALSE,
6357     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6358   },
6359   {
6360     Xamoeba_3,                          FALSE,  FALSE,
6361     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6362   },
6363   {
6364     Xamoeba_4,                          FALSE,  FALSE,
6365     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6366   },
6367   {
6368     Xamoeba_5,                          TRUE,   FALSE,
6369     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6370   },
6371   {
6372     Xamoeba_6,                          FALSE,  FALSE,
6373     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6374   },
6375   {
6376     Xamoeba_7,                          FALSE,  FALSE,
6377     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6378   },
6379   {
6380     Xamoeba_8,                          FALSE,  FALSE,
6381     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6382   },
6383   {
6384     Xdoor_1,                            TRUE,   FALSE,
6385     EL_EM_GATE_1,                       -1, -1
6386   },
6387   {
6388     Xdoor_2,                            TRUE,   FALSE,
6389     EL_EM_GATE_2,                       -1, -1
6390   },
6391   {
6392     Xdoor_3,                            TRUE,   FALSE,
6393     EL_EM_GATE_3,                       -1, -1
6394   },
6395   {
6396     Xdoor_4,                            TRUE,   FALSE,
6397     EL_EM_GATE_4,                       -1, -1
6398   },
6399   {
6400     Xdoor_5,                            TRUE,   FALSE,
6401     EL_EMC_GATE_5,                      -1, -1
6402   },
6403   {
6404     Xdoor_6,                            TRUE,   FALSE,
6405     EL_EMC_GATE_6,                      -1, -1
6406   },
6407   {
6408     Xdoor_7,                            TRUE,   FALSE,
6409     EL_EMC_GATE_7,                      -1, -1
6410   },
6411   {
6412     Xdoor_8,                            TRUE,   FALSE,
6413     EL_EMC_GATE_8,                      -1, -1
6414   },
6415   {
6416     Xkey_1,                             TRUE,   FALSE,
6417     EL_EM_KEY_1,                        -1, -1
6418   },
6419   {
6420     Xkey_2,                             TRUE,   FALSE,
6421     EL_EM_KEY_2,                        -1, -1
6422   },
6423   {
6424     Xkey_3,                             TRUE,   FALSE,
6425     EL_EM_KEY_3,                        -1, -1
6426   },
6427   {
6428     Xkey_4,                             TRUE,   FALSE,
6429     EL_EM_KEY_4,                        -1, -1
6430   },
6431   {
6432     Xkey_5,                             TRUE,   FALSE,
6433     EL_EMC_KEY_5,                       -1, -1
6434   },
6435   {
6436     Xkey_6,                             TRUE,   FALSE,
6437     EL_EMC_KEY_6,                       -1, -1
6438   },
6439   {
6440     Xkey_7,                             TRUE,   FALSE,
6441     EL_EMC_KEY_7,                       -1, -1
6442   },
6443   {
6444     Xkey_8,                             TRUE,   FALSE,
6445     EL_EMC_KEY_8,                       -1, -1
6446   },
6447   {
6448     Xwind_n,                            TRUE,   FALSE,
6449     EL_BALLOON_SWITCH_UP,               -1, -1
6450   },
6451   {
6452     Xwind_e,                            TRUE,   FALSE,
6453     EL_BALLOON_SWITCH_RIGHT,            -1, -1
6454   },
6455   {
6456     Xwind_s,                            TRUE,   FALSE,
6457     EL_BALLOON_SWITCH_DOWN,             -1, -1
6458   },
6459   {
6460     Xwind_w,                            TRUE,   FALSE,
6461     EL_BALLOON_SWITCH_LEFT,             -1, -1
6462   },
6463   {
6464     Xwind_nesw,                         TRUE,   FALSE,
6465     EL_BALLOON_SWITCH_ANY,              -1, -1
6466   },
6467   {
6468     Xwind_stop,                         TRUE,   FALSE,
6469     EL_BALLOON_SWITCH_NONE,             -1, -1
6470   },
6471   {
6472     Xexit,                              TRUE,   FALSE,
6473     EL_EM_EXIT_CLOSED,                  -1, -1
6474   },
6475   {
6476     Xexit_1,                            TRUE,   FALSE,
6477     EL_EM_EXIT_OPEN,                    -1, -1
6478   },
6479   {
6480     Xexit_2,                            FALSE,  FALSE,
6481     EL_EM_EXIT_OPEN,                    -1, -1
6482   },
6483   {
6484     Xexit_3,                            FALSE,  FALSE,
6485     EL_EM_EXIT_OPEN,                    -1, -1
6486   },
6487   {
6488     Xdynamite,                          TRUE,   FALSE,
6489     EL_EM_DYNAMITE,                     -1, -1
6490   },
6491   {
6492     Ydynamite_eat,                      FALSE,  FALSE,
6493     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6494   },
6495   {
6496     Xdynamite_1,                        TRUE,   FALSE,
6497     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6498   },
6499   {
6500     Xdynamite_2,                        FALSE,  FALSE,
6501     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6502   },
6503   {
6504     Xdynamite_3,                        FALSE,  FALSE,
6505     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6506   },
6507   {
6508     Xdynamite_4,                        FALSE,  FALSE,
6509     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6510   },
6511   {
6512     Xbumper,                            TRUE,   FALSE,
6513     EL_EMC_SPRING_BUMPER,               -1, -1
6514   },
6515   {
6516     XbumperB,                           FALSE,  FALSE,
6517     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
6518   },
6519   {
6520     Xwheel,                             TRUE,   FALSE,
6521     EL_ROBOT_WHEEL,                     -1, -1
6522   },
6523   {
6524     XwheelB,                            FALSE,  FALSE,
6525     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
6526   },
6527   {
6528     Xswitch,                            TRUE,   FALSE,
6529     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
6530   },
6531   {
6532     XswitchB,                           FALSE,  FALSE,
6533     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
6534   },
6535   {
6536     Xsand,                              TRUE,   FALSE,
6537     EL_QUICKSAND_EMPTY,                 -1, -1
6538   },
6539   {
6540     Xsand_stone,                        TRUE,   FALSE,
6541     EL_QUICKSAND_FULL,                  -1, -1
6542   },
6543   {
6544     Xsand_stonein_1,                    FALSE,  TRUE,
6545     EL_ROCK,                            ACTION_FILLING, -1
6546   },
6547   {
6548     Xsand_stonein_2,                    FALSE,  TRUE,
6549     EL_ROCK,                            ACTION_FILLING, -1
6550   },
6551   {
6552     Xsand_stonein_3,                    FALSE,  TRUE,
6553     EL_ROCK,                            ACTION_FILLING, -1
6554   },
6555   {
6556     Xsand_stonein_4,                    FALSE,  TRUE,
6557     EL_ROCK,                            ACTION_FILLING, -1
6558   },
6559   {
6560     Xsand_stonesand_1,                  FALSE,  FALSE,
6561     EL_QUICKSAND_EMPTYING,              -1, -1
6562   },
6563   {
6564     Xsand_stonesand_2,                  FALSE,  FALSE,
6565     EL_QUICKSAND_EMPTYING,              -1, -1
6566   },
6567   {
6568     Xsand_stonesand_3,                  FALSE,  FALSE,
6569     EL_QUICKSAND_EMPTYING,              -1, -1
6570   },
6571   {
6572     Xsand_stonesand_4,                  FALSE,  FALSE,
6573     EL_QUICKSAND_EMPTYING,              -1, -1
6574   },
6575   {
6576     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
6577     EL_QUICKSAND_EMPTYING,              -1, -1
6578   },
6579   {
6580     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
6581     EL_QUICKSAND_EMPTYING,              -1, -1
6582   },
6583   {
6584     Xsand_stoneout_1,                   FALSE,  FALSE,
6585     EL_ROCK,                            ACTION_EMPTYING, -1
6586   },
6587   {
6588     Xsand_stoneout_2,                   FALSE,  FALSE,
6589     EL_ROCK,                            ACTION_EMPTYING, -1
6590   },
6591   {
6592     Xsand_sandstone_1,                  FALSE,  FALSE,
6593     EL_QUICKSAND_FILLING,               -1, -1
6594   },
6595   {
6596     Xsand_sandstone_2,                  FALSE,  FALSE,
6597     EL_QUICKSAND_FILLING,               -1, -1
6598   },
6599   {
6600     Xsand_sandstone_3,                  FALSE,  FALSE,
6601     EL_QUICKSAND_FILLING,               -1, -1
6602   },
6603   {
6604     Xsand_sandstone_4,                  FALSE,  FALSE,
6605     EL_QUICKSAND_FILLING,               -1, -1
6606   },
6607   {
6608     Xplant,                             TRUE,   FALSE,
6609     EL_EMC_PLANT,                       -1, -1
6610   },
6611   {
6612     Yplant,                             FALSE,  FALSE,
6613     EL_EMC_PLANT,                       -1, -1
6614   },
6615   {
6616     Xlenses,                            TRUE,   FALSE,
6617     EL_EMC_LENSES,                      -1, -1
6618   },
6619   {
6620     Xmagnify,                           TRUE,   FALSE,
6621     EL_EMC_MAGNIFIER,                   -1, -1
6622   },
6623   {
6624     Xdripper,                           TRUE,   FALSE,
6625     EL_EMC_DRIPPER,                     -1, -1
6626   },
6627   {
6628     XdripperB,                          FALSE,  FALSE,
6629     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
6630   },
6631   {
6632     Xfake_blank,                        TRUE,   FALSE,
6633     EL_INVISIBLE_WALL,                  -1, -1
6634   },
6635   {
6636     Xfake_blankB,                       FALSE,  FALSE,
6637     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
6638   },
6639   {
6640     Xfake_grass,                        TRUE,   FALSE,
6641     EL_EMC_FAKE_GRASS,                  -1, -1
6642   },
6643   {
6644     Xfake_grassB,                       FALSE,  FALSE,
6645     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
6646   },
6647   {
6648     Xfake_door_1,                       TRUE,   FALSE,
6649     EL_EM_GATE_1_GRAY,                  -1, -1
6650   },
6651   {
6652     Xfake_door_2,                       TRUE,   FALSE,
6653     EL_EM_GATE_2_GRAY,                  -1, -1
6654   },
6655   {
6656     Xfake_door_3,                       TRUE,   FALSE,
6657     EL_EM_GATE_3_GRAY,                  -1, -1
6658   },
6659   {
6660     Xfake_door_4,                       TRUE,   FALSE,
6661     EL_EM_GATE_4_GRAY,                  -1, -1
6662   },
6663   {
6664     Xfake_door_5,                       TRUE,   FALSE,
6665     EL_EMC_GATE_5_GRAY,                 -1, -1
6666   },
6667   {
6668     Xfake_door_6,                       TRUE,   FALSE,
6669     EL_EMC_GATE_6_GRAY,                 -1, -1
6670   },
6671   {
6672     Xfake_door_7,                       TRUE,   FALSE,
6673     EL_EMC_GATE_7_GRAY,                 -1, -1
6674   },
6675   {
6676     Xfake_door_8,                       TRUE,   FALSE,
6677     EL_EMC_GATE_8_GRAY,                 -1, -1
6678   },
6679   {
6680     Xfake_acid_1,                       TRUE,   FALSE,
6681     EL_EMC_FAKE_ACID,                   -1, -1
6682   },
6683   {
6684     Xfake_acid_2,                       FALSE,  FALSE,
6685     EL_EMC_FAKE_ACID,                   -1, -1
6686   },
6687   {
6688     Xfake_acid_3,                       FALSE,  FALSE,
6689     EL_EMC_FAKE_ACID,                   -1, -1
6690   },
6691   {
6692     Xfake_acid_4,                       FALSE,  FALSE,
6693     EL_EMC_FAKE_ACID,                   -1, -1
6694   },
6695   {
6696     Xfake_acid_5,                       FALSE,  FALSE,
6697     EL_EMC_FAKE_ACID,                   -1, -1
6698   },
6699   {
6700     Xfake_acid_6,                       FALSE,  FALSE,
6701     EL_EMC_FAKE_ACID,                   -1, -1
6702   },
6703   {
6704     Xfake_acid_7,                       FALSE,  FALSE,
6705     EL_EMC_FAKE_ACID,                   -1, -1
6706   },
6707   {
6708     Xfake_acid_8,                       FALSE,  FALSE,
6709     EL_EMC_FAKE_ACID,                   -1, -1
6710   },
6711   {
6712     Xsteel_1,                           TRUE,   FALSE,
6713     EL_STEELWALL,                       -1, -1
6714   },
6715   {
6716     Xsteel_2,                           TRUE,   FALSE,
6717     EL_EMC_STEELWALL_2,                 -1, -1
6718   },
6719   {
6720     Xsteel_3,                           TRUE,   FALSE,
6721     EL_EMC_STEELWALL_3,                 -1, -1
6722   },
6723   {
6724     Xsteel_4,                           TRUE,   FALSE,
6725     EL_EMC_STEELWALL_4,                 -1, -1
6726   },
6727   {
6728     Xwall_1,                            TRUE,   FALSE,
6729     EL_WALL,                            -1, -1
6730   },
6731   {
6732     Xwall_2,                            TRUE,   FALSE,
6733     EL_EMC_WALL_14,                     -1, -1
6734   },
6735   {
6736     Xwall_3,                            TRUE,   FALSE,
6737     EL_EMC_WALL_15,                     -1, -1
6738   },
6739   {
6740     Xwall_4,                            TRUE,   FALSE,
6741     EL_EMC_WALL_16,                     -1, -1
6742   },
6743   {
6744     Xround_wall_1,                      TRUE,   FALSE,
6745     EL_WALL_SLIPPERY,                   -1, -1
6746   },
6747   {
6748     Xround_wall_2,                      TRUE,   FALSE,
6749     EL_EMC_WALL_SLIPPERY_2,             -1, -1
6750   },
6751   {
6752     Xround_wall_3,                      TRUE,   FALSE,
6753     EL_EMC_WALL_SLIPPERY_3,             -1, -1
6754   },
6755   {
6756     Xround_wall_4,                      TRUE,   FALSE,
6757     EL_EMC_WALL_SLIPPERY_4,             -1, -1
6758   },
6759   {
6760     Xdecor_1,                           TRUE,   FALSE,
6761     EL_EMC_WALL_8,                      -1, -1
6762   },
6763   {
6764     Xdecor_2,                           TRUE,   FALSE,
6765     EL_EMC_WALL_6,                      -1, -1
6766   },
6767   {
6768     Xdecor_3,                           TRUE,   FALSE,
6769     EL_EMC_WALL_4,                      -1, -1
6770   },
6771   {
6772     Xdecor_4,                           TRUE,   FALSE,
6773     EL_EMC_WALL_7,                      -1, -1
6774   },
6775   {
6776     Xdecor_5,                           TRUE,   FALSE,
6777     EL_EMC_WALL_5,                      -1, -1
6778   },
6779   {
6780     Xdecor_6,                           TRUE,   FALSE,
6781     EL_EMC_WALL_9,                      -1, -1
6782   },
6783   {
6784     Xdecor_7,                           TRUE,   FALSE,
6785     EL_EMC_WALL_10,                     -1, -1
6786   },
6787   {
6788     Xdecor_8,                           TRUE,   FALSE,
6789     EL_EMC_WALL_1,                      -1, -1
6790   },
6791   {
6792     Xdecor_9,                           TRUE,   FALSE,
6793     EL_EMC_WALL_2,                      -1, -1
6794   },
6795   {
6796     Xdecor_10,                          TRUE,   FALSE,
6797     EL_EMC_WALL_3,                      -1, -1
6798   },
6799   {
6800     Xdecor_11,                          TRUE,   FALSE,
6801     EL_EMC_WALL_11,                     -1, -1
6802   },
6803   {
6804     Xdecor_12,                          TRUE,   FALSE,
6805     EL_EMC_WALL_12,                     -1, -1
6806   },
6807   {
6808     Xalpha_0,                           TRUE,   FALSE,
6809     EL_CHAR('0'),                       -1, -1
6810   },
6811   {
6812     Xalpha_1,                           TRUE,   FALSE,
6813     EL_CHAR('1'),                       -1, -1
6814   },
6815   {
6816     Xalpha_2,                           TRUE,   FALSE,
6817     EL_CHAR('2'),                       -1, -1
6818   },
6819   {
6820     Xalpha_3,                           TRUE,   FALSE,
6821     EL_CHAR('3'),                       -1, -1
6822   },
6823   {
6824     Xalpha_4,                           TRUE,   FALSE,
6825     EL_CHAR('4'),                       -1, -1
6826   },
6827   {
6828     Xalpha_5,                           TRUE,   FALSE,
6829     EL_CHAR('5'),                       -1, -1
6830   },
6831   {
6832     Xalpha_6,                           TRUE,   FALSE,
6833     EL_CHAR('6'),                       -1, -1
6834   },
6835   {
6836     Xalpha_7,                           TRUE,   FALSE,
6837     EL_CHAR('7'),                       -1, -1
6838   },
6839   {
6840     Xalpha_8,                           TRUE,   FALSE,
6841     EL_CHAR('8'),                       -1, -1
6842   },
6843   {
6844     Xalpha_9,                           TRUE,   FALSE,
6845     EL_CHAR('9'),                       -1, -1
6846   },
6847   {
6848     Xalpha_excla,                       TRUE,   FALSE,
6849     EL_CHAR('!'),                       -1, -1
6850   },
6851   {
6852     Xalpha_quote,                       TRUE,   FALSE,
6853     EL_CHAR('"'),                       -1, -1
6854   },
6855   {
6856     Xalpha_comma,                       TRUE,   FALSE,
6857     EL_CHAR(','),                       -1, -1
6858   },
6859   {
6860     Xalpha_minus,                       TRUE,   FALSE,
6861     EL_CHAR('-'),                       -1, -1
6862   },
6863   {
6864     Xalpha_perio,                       TRUE,   FALSE,
6865     EL_CHAR('.'),                       -1, -1
6866   },
6867   {
6868     Xalpha_colon,                       TRUE,   FALSE,
6869     EL_CHAR(':'),                       -1, -1
6870   },
6871   {
6872     Xalpha_quest,                       TRUE,   FALSE,
6873     EL_CHAR('?'),                       -1, -1
6874   },
6875   {
6876     Xalpha_a,                           TRUE,   FALSE,
6877     EL_CHAR('A'),                       -1, -1
6878   },
6879   {
6880     Xalpha_b,                           TRUE,   FALSE,
6881     EL_CHAR('B'),                       -1, -1
6882   },
6883   {
6884     Xalpha_c,                           TRUE,   FALSE,
6885     EL_CHAR('C'),                       -1, -1
6886   },
6887   {
6888     Xalpha_d,                           TRUE,   FALSE,
6889     EL_CHAR('D'),                       -1, -1
6890   },
6891   {
6892     Xalpha_e,                           TRUE,   FALSE,
6893     EL_CHAR('E'),                       -1, -1
6894   },
6895   {
6896     Xalpha_f,                           TRUE,   FALSE,
6897     EL_CHAR('F'),                       -1, -1
6898   },
6899   {
6900     Xalpha_g,                           TRUE,   FALSE,
6901     EL_CHAR('G'),                       -1, -1
6902   },
6903   {
6904     Xalpha_h,                           TRUE,   FALSE,
6905     EL_CHAR('H'),                       -1, -1
6906   },
6907   {
6908     Xalpha_i,                           TRUE,   FALSE,
6909     EL_CHAR('I'),                       -1, -1
6910   },
6911   {
6912     Xalpha_j,                           TRUE,   FALSE,
6913     EL_CHAR('J'),                       -1, -1
6914   },
6915   {
6916     Xalpha_k,                           TRUE,   FALSE,
6917     EL_CHAR('K'),                       -1, -1
6918   },
6919   {
6920     Xalpha_l,                           TRUE,   FALSE,
6921     EL_CHAR('L'),                       -1, -1
6922   },
6923   {
6924     Xalpha_m,                           TRUE,   FALSE,
6925     EL_CHAR('M'),                       -1, -1
6926   },
6927   {
6928     Xalpha_n,                           TRUE,   FALSE,
6929     EL_CHAR('N'),                       -1, -1
6930   },
6931   {
6932     Xalpha_o,                           TRUE,   FALSE,
6933     EL_CHAR('O'),                       -1, -1
6934   },
6935   {
6936     Xalpha_p,                           TRUE,   FALSE,
6937     EL_CHAR('P'),                       -1, -1
6938   },
6939   {
6940     Xalpha_q,                           TRUE,   FALSE,
6941     EL_CHAR('Q'),                       -1, -1
6942   },
6943   {
6944     Xalpha_r,                           TRUE,   FALSE,
6945     EL_CHAR('R'),                       -1, -1
6946   },
6947   {
6948     Xalpha_s,                           TRUE,   FALSE,
6949     EL_CHAR('S'),                       -1, -1
6950   },
6951   {
6952     Xalpha_t,                           TRUE,   FALSE,
6953     EL_CHAR('T'),                       -1, -1
6954   },
6955   {
6956     Xalpha_u,                           TRUE,   FALSE,
6957     EL_CHAR('U'),                       -1, -1
6958   },
6959   {
6960     Xalpha_v,                           TRUE,   FALSE,
6961     EL_CHAR('V'),                       -1, -1
6962   },
6963   {
6964     Xalpha_w,                           TRUE,   FALSE,
6965     EL_CHAR('W'),                       -1, -1
6966   },
6967   {
6968     Xalpha_x,                           TRUE,   FALSE,
6969     EL_CHAR('X'),                       -1, -1
6970   },
6971   {
6972     Xalpha_y,                           TRUE,   FALSE,
6973     EL_CHAR('Y'),                       -1, -1
6974   },
6975   {
6976     Xalpha_z,                           TRUE,   FALSE,
6977     EL_CHAR('Z'),                       -1, -1
6978   },
6979   {
6980     Xalpha_arrow_e,                     TRUE,   FALSE,
6981     EL_CHAR('>'),                       -1, -1
6982   },
6983   {
6984     Xalpha_arrow_w,                     TRUE,   FALSE,
6985     EL_CHAR('<'),                       -1, -1
6986   },
6987   {
6988     Xalpha_copyr,                       TRUE,   FALSE,
6989     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
6990   },
6991
6992   {
6993     Xboom_bug,                          FALSE,  FALSE,
6994     EL_BUG,                             ACTION_EXPLODING, -1
6995   },
6996   {
6997     Xboom_bomb,                         FALSE,  FALSE,
6998     EL_BOMB,                            ACTION_EXPLODING, -1
6999   },
7000   {
7001     Xboom_android,                      FALSE,  FALSE,
7002     EL_EMC_ANDROID,                     ACTION_OTHER, -1
7003   },
7004   {
7005     Xboom_1,                            FALSE,  FALSE,
7006     EL_DEFAULT,                         ACTION_EXPLODING, -1
7007   },
7008   {
7009     Xboom_2,                            FALSE,  FALSE,
7010     EL_DEFAULT,                         ACTION_EXPLODING, -1
7011   },
7012   {
7013     Znormal,                            FALSE,  FALSE,
7014     EL_EMPTY,                           -1, -1
7015   },
7016   {
7017     Zdynamite,                          FALSE,  FALSE,
7018     EL_EMPTY,                           -1, -1
7019   },
7020   {
7021     Zplayer,                            FALSE,  FALSE,
7022     EL_EMPTY,                           -1, -1
7023   },
7024   {
7025     ZBORDER,                            FALSE,  FALSE,
7026     EL_EMPTY,                           -1, -1
7027   },
7028
7029   {
7030     -1,                                 FALSE,  FALSE,
7031     -1,                                 -1, -1
7032   }
7033 };
7034
7035 static struct Mapping_EM_to_RND_player
7036 {
7037   int action_em;
7038   int player_nr;
7039
7040   int element_rnd;
7041   int action;
7042   int direction;
7043 }
7044 em_player_mapping_list[] =
7045 {
7046   {
7047     SPR_walk + 0,                       0,
7048     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
7049   },
7050   {
7051     SPR_walk + 1,                       0,
7052     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
7053   },
7054   {
7055     SPR_walk + 2,                       0,
7056     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
7057   },
7058   {
7059     SPR_walk + 3,                       0,
7060     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
7061   },
7062   {
7063     SPR_push + 0,                       0,
7064     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
7065   },
7066   {
7067     SPR_push + 1,                       0,
7068     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
7069   },
7070   {
7071     SPR_push + 2,                       0,
7072     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
7073   },
7074   {
7075     SPR_push + 3,                       0,
7076     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
7077   },
7078   {
7079     SPR_spray + 0,                      0,
7080     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
7081   },
7082   {
7083     SPR_spray + 1,                      0,
7084     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7085   },
7086   {
7087     SPR_spray + 2,                      0,
7088     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
7089   },
7090   {
7091     SPR_spray + 3,                      0,
7092     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
7093   },
7094   {
7095     SPR_walk + 0,                       1,
7096     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
7097   },
7098   {
7099     SPR_walk + 1,                       1,
7100     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
7101   },
7102   {
7103     SPR_walk + 2,                       1,
7104     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
7105   },
7106   {
7107     SPR_walk + 3,                       1,
7108     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
7109   },
7110   {
7111     SPR_push + 0,                       1,
7112     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
7113   },
7114   {
7115     SPR_push + 1,                       1,
7116     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
7117   },
7118   {
7119     SPR_push + 2,                       1,
7120     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
7121   },
7122   {
7123     SPR_push + 3,                       1,
7124     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
7125   },
7126   {
7127     SPR_spray + 0,                      1,
7128     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
7129   },
7130   {
7131     SPR_spray + 1,                      1,
7132     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7133   },
7134   {
7135     SPR_spray + 2,                      1,
7136     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
7137   },
7138   {
7139     SPR_spray + 3,                      1,
7140     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
7141   },
7142   {
7143     SPR_still,                          0,
7144     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
7145   },
7146   {
7147     SPR_still,                          1,
7148     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
7149   },
7150   {
7151     SPR_walk + 0,                       2,
7152     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
7153   },
7154   {
7155     SPR_walk + 1,                       2,
7156     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
7157   },
7158   {
7159     SPR_walk + 2,                       2,
7160     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
7161   },
7162   {
7163     SPR_walk + 3,                       2,
7164     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
7165   },
7166   {
7167     SPR_push + 0,                       2,
7168     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
7169   },
7170   {
7171     SPR_push + 1,                       2,
7172     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
7173   },
7174   {
7175     SPR_push + 2,                       2,
7176     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
7177   },
7178   {
7179     SPR_push + 3,                       2,
7180     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
7181   },
7182   {
7183     SPR_spray + 0,                      2,
7184     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
7185   },
7186   {
7187     SPR_spray + 1,                      2,
7188     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7189   },
7190   {
7191     SPR_spray + 2,                      2,
7192     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
7193   },
7194   {
7195     SPR_spray + 3,                      2,
7196     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
7197   },
7198   {
7199     SPR_walk + 0,                       3,
7200     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
7201   },
7202   {
7203     SPR_walk + 1,                       3,
7204     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
7205   },
7206   {
7207     SPR_walk + 2,                       3,
7208     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
7209   },
7210   {
7211     SPR_walk + 3,                       3,
7212     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
7213   },
7214   {
7215     SPR_push + 0,                       3,
7216     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
7217   },
7218   {
7219     SPR_push + 1,                       3,
7220     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
7221   },
7222   {
7223     SPR_push + 2,                       3,
7224     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
7225   },
7226   {
7227     SPR_push + 3,                       3,
7228     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
7229   },
7230   {
7231     SPR_spray + 0,                      3,
7232     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
7233   },
7234   {
7235     SPR_spray + 1,                      3,
7236     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7237   },
7238   {
7239     SPR_spray + 2,                      3,
7240     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
7241   },
7242   {
7243     SPR_spray + 3,                      3,
7244     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
7245   },
7246   {
7247     SPR_still,                          2,
7248     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
7249   },
7250   {
7251     SPR_still,                          3,
7252     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7253   },
7254
7255   {
7256     -1,                                 -1,
7257     -1,                                 -1, -1
7258   }
7259 };
7260
7261 int map_element_RND_to_EM(int element_rnd)
7262 {
7263   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7264   static boolean mapping_initialized = FALSE;
7265
7266   if (!mapping_initialized)
7267   {
7268     int i;
7269
7270     /* return "Xalpha_quest" for all undefined elements in mapping array */
7271     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7272       mapping_RND_to_EM[i] = Xalpha_quest;
7273
7274     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7275       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7276         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7277           em_object_mapping_list[i].element_em;
7278
7279     mapping_initialized = TRUE;
7280   }
7281
7282   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7283     return mapping_RND_to_EM[element_rnd];
7284
7285   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7286
7287   return EL_UNKNOWN;
7288 }
7289
7290 int map_element_EM_to_RND(int element_em)
7291 {
7292   static unsigned short mapping_EM_to_RND[TILE_MAX];
7293   static boolean mapping_initialized = FALSE;
7294
7295   if (!mapping_initialized)
7296   {
7297     int i;
7298
7299     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7300     for (i = 0; i < TILE_MAX; i++)
7301       mapping_EM_to_RND[i] = EL_UNKNOWN;
7302
7303     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7304       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7305         em_object_mapping_list[i].element_rnd;
7306
7307     mapping_initialized = TRUE;
7308   }
7309
7310   if (element_em >= 0 && element_em < TILE_MAX)
7311     return mapping_EM_to_RND[element_em];
7312
7313   Error(ERR_WARN, "invalid EM level element %d", element_em);
7314
7315   return EL_UNKNOWN;
7316 }
7317
7318 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7319 {
7320   struct LevelInfo_EM *level_em = level->native_em_level;
7321   struct LEVEL *lev = level_em->lev;
7322   int i, j;
7323
7324   for (i = 0; i < TILE_MAX; i++)
7325     lev->android_array[i] = Xblank;
7326
7327   for (i = 0; i < level->num_android_clone_elements; i++)
7328   {
7329     int element_rnd = level->android_clone_element[i];
7330     int element_em = map_element_RND_to_EM(element_rnd);
7331
7332     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7333       if (em_object_mapping_list[j].element_rnd == element_rnd)
7334         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7335   }
7336 }
7337
7338 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7339 {
7340   struct LevelInfo_EM *level_em = level->native_em_level;
7341   struct LEVEL *lev = level_em->lev;
7342   int i, j;
7343
7344   level->num_android_clone_elements = 0;
7345
7346   for (i = 0; i < TILE_MAX; i++)
7347   {
7348     int element_em = lev->android_array[i];
7349     int element_rnd;
7350     boolean element_found = FALSE;
7351
7352     if (element_em == Xblank)
7353       continue;
7354
7355     element_rnd = map_element_EM_to_RND(element_em);
7356
7357     for (j = 0; j < level->num_android_clone_elements; j++)
7358       if (level->android_clone_element[j] == element_rnd)
7359         element_found = TRUE;
7360
7361     if (!element_found)
7362     {
7363       level->android_clone_element[level->num_android_clone_elements++] =
7364         element_rnd;
7365
7366       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7367         break;
7368     }
7369   }
7370
7371   if (level->num_android_clone_elements == 0)
7372   {
7373     level->num_android_clone_elements = 1;
7374     level->android_clone_element[0] = EL_EMPTY;
7375   }
7376 }
7377
7378 int map_direction_RND_to_EM(int direction)
7379 {
7380   return (direction == MV_UP    ? 0 :
7381           direction == MV_RIGHT ? 1 :
7382           direction == MV_DOWN  ? 2 :
7383           direction == MV_LEFT  ? 3 :
7384           -1);
7385 }
7386
7387 int map_direction_EM_to_RND(int direction)
7388 {
7389   return (direction == 0 ? MV_UP    :
7390           direction == 1 ? MV_RIGHT :
7391           direction == 2 ? MV_DOWN  :
7392           direction == 3 ? MV_LEFT  :
7393           MV_NONE);
7394 }
7395
7396 int map_element_RND_to_SP(int element_rnd)
7397 {
7398   int element_sp = 0x20;        /* map unknown elements to yellow "hardware" */
7399
7400   if (element_rnd >= EL_SP_START &&
7401       element_rnd <= EL_SP_END)
7402     element_sp = element_rnd - EL_SP_START;
7403   else if (element_rnd == EL_EMPTY_SPACE)
7404     element_sp = 0x00;
7405   else if (element_rnd == EL_INVISIBLE_WALL)
7406     element_sp = 0x28;
7407
7408   return element_sp;
7409 }
7410
7411 int map_element_SP_to_RND(int element_sp)
7412 {
7413   int element_rnd = EL_UNKNOWN;
7414
7415   if (element_sp >= 0x00 &&
7416       element_sp <= 0x27)
7417     element_rnd = EL_SP_START + element_sp;
7418   else if (element_sp == 0x28)
7419     element_rnd = EL_INVISIBLE_WALL;
7420
7421   return element_rnd;
7422 }
7423
7424 int map_action_SP_to_RND(int action_sp)
7425 {
7426   switch (action_sp)
7427   {
7428     case actActive:             return ACTION_ACTIVE;
7429     case actImpact:             return ACTION_IMPACT;
7430     case actExploding:          return ACTION_EXPLODING;
7431     case actDigging:            return ACTION_DIGGING;
7432     case actSnapping:           return ACTION_SNAPPING;
7433     case actCollecting:         return ACTION_COLLECTING;
7434     case actPassing:            return ACTION_PASSING;
7435     case actPushing:            return ACTION_PUSHING;
7436     case actDropping:           return ACTION_DROPPING;
7437
7438     default:                    return ACTION_DEFAULT;
7439   }
7440 }
7441
7442 int map_element_RND_to_MM(int element_rnd)
7443 {
7444   return (element_rnd >= EL_MM_START_1 &&
7445           element_rnd <= EL_MM_END_1 ?
7446           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7447
7448           element_rnd >= EL_MM_START_2 &&
7449           element_rnd <= EL_MM_END_2 ?
7450           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7451
7452           element_rnd >= EL_CHAR_START &&
7453           element_rnd <= EL_CHAR_END ?
7454           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7455
7456           element_rnd >= EL_MM_RUNTIME_START &&
7457           element_rnd <= EL_MM_RUNTIME_END ?
7458           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7459
7460           element_rnd >= EL_MM_DUMMY_START &&
7461           element_rnd <= EL_MM_DUMMY_END ?
7462           EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7463
7464           EL_MM_EMPTY_NATIVE);
7465 }
7466
7467 int map_element_MM_to_RND(int element_mm)
7468 {
7469   return (element_mm == EL_MM_EMPTY_NATIVE ||
7470           element_mm == EL_DF_EMPTY_NATIVE ?
7471           EL_EMPTY :
7472
7473           element_mm >= EL_MM_START_1_NATIVE &&
7474           element_mm <= EL_MM_END_1_NATIVE ?
7475           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7476
7477           element_mm >= EL_MM_START_2_NATIVE &&
7478           element_mm <= EL_MM_END_2_NATIVE ?
7479           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7480
7481           element_mm >= EL_MM_CHAR_START_NATIVE &&
7482           element_mm <= EL_MM_CHAR_END_NATIVE ?
7483           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7484
7485           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7486           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7487           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7488
7489           element_mm >= EL_MM_DUMMY_START_NATIVE &&
7490           element_mm <= EL_MM_DUMMY_END_NATIVE ?
7491           EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7492
7493           EL_EMPTY);
7494 }
7495
7496 int map_action_MM_to_RND(int action_mm)
7497 {
7498   /* all MM actions are defined to exactly match their RND counterparts */
7499   return action_mm;
7500 }
7501
7502 int map_sound_MM_to_RND(int sound_mm)
7503 {
7504   switch (sound_mm)
7505   {
7506     case SND_MM_GAME_LEVELTIME_CHARGING:
7507       return SND_GAME_LEVELTIME_CHARGING;
7508
7509     case SND_MM_GAME_HEALTH_CHARGING:
7510       return SND_GAME_HEALTH_CHARGING;
7511
7512     default:
7513       return SND_UNDEFINED;
7514   }
7515 }
7516
7517 int map_mm_wall_element(int element)
7518 {
7519   return (element >= EL_MM_STEEL_WALL_START &&
7520           element <= EL_MM_STEEL_WALL_END ?
7521           EL_MM_STEEL_WALL :
7522
7523           element >= EL_MM_WOODEN_WALL_START &&
7524           element <= EL_MM_WOODEN_WALL_END ?
7525           EL_MM_WOODEN_WALL :
7526
7527           element >= EL_MM_ICE_WALL_START &&
7528           element <= EL_MM_ICE_WALL_END ?
7529           EL_MM_ICE_WALL :
7530
7531           element >= EL_MM_AMOEBA_WALL_START &&
7532           element <= EL_MM_AMOEBA_WALL_END ?
7533           EL_MM_AMOEBA_WALL :
7534
7535           element >= EL_DF_STEEL_WALL_START &&
7536           element <= EL_DF_STEEL_WALL_END ?
7537           EL_DF_STEEL_WALL :
7538
7539           element >= EL_DF_WOODEN_WALL_START &&
7540           element <= EL_DF_WOODEN_WALL_END ?
7541           EL_DF_WOODEN_WALL :
7542
7543           element);
7544 }
7545
7546 int map_mm_wall_element_editor(int element)
7547 {
7548   switch (element)
7549   {
7550     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
7551     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
7552     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
7553     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
7554     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
7555     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
7556
7557     default:                    return element;
7558   }
7559 }
7560
7561 int get_next_element(int element)
7562 {
7563   switch (element)
7564   {
7565     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
7566     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
7567     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
7568     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
7569     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
7570     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
7571     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
7572     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
7573     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
7574     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
7575     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
7576
7577     default:                            return element;
7578   }
7579 }
7580
7581 int el2img_mm(int element_mm)
7582 {
7583   return el2img(map_element_MM_to_RND(element_mm));
7584 }
7585
7586 int el_act_dir2img(int element, int action, int direction)
7587 {
7588   element = GFX_ELEMENT(element);
7589   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7590
7591   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7592   return element_info[element].direction_graphic[action][direction];
7593 }
7594
7595 static int el_act_dir2crm(int element, int action, int direction)
7596 {
7597   element = GFX_ELEMENT(element);
7598   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7599
7600   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7601   return element_info[element].direction_crumbled[action][direction];
7602 }
7603
7604 int el_act2img(int element, int action)
7605 {
7606   element = GFX_ELEMENT(element);
7607
7608   return element_info[element].graphic[action];
7609 }
7610
7611 int el_act2crm(int element, int action)
7612 {
7613   element = GFX_ELEMENT(element);
7614
7615   return element_info[element].crumbled[action];
7616 }
7617
7618 int el_dir2img(int element, int direction)
7619 {
7620   element = GFX_ELEMENT(element);
7621
7622   return el_act_dir2img(element, ACTION_DEFAULT, direction);
7623 }
7624
7625 int el2baseimg(int element)
7626 {
7627   return element_info[element].graphic[ACTION_DEFAULT];
7628 }
7629
7630 int el2img(int element)
7631 {
7632   element = GFX_ELEMENT(element);
7633
7634   return element_info[element].graphic[ACTION_DEFAULT];
7635 }
7636
7637 int el2edimg(int element)
7638 {
7639   element = GFX_ELEMENT(element);
7640
7641   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7642 }
7643
7644 int el2preimg(int element)
7645 {
7646   element = GFX_ELEMENT(element);
7647
7648   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7649 }
7650
7651 int el2panelimg(int element)
7652 {
7653   element = GFX_ELEMENT(element);
7654
7655   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7656 }
7657
7658 int font2baseimg(int font_nr)
7659 {
7660   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7661 }
7662
7663 int getBeltNrFromBeltElement(int element)
7664 {
7665   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7666           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7667           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7668 }
7669
7670 int getBeltNrFromBeltActiveElement(int element)
7671 {
7672   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7673           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7674           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7675 }
7676
7677 int getBeltNrFromBeltSwitchElement(int element)
7678 {
7679   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7680           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7681           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7682 }
7683
7684 int getBeltDirNrFromBeltElement(int element)
7685 {
7686   static int belt_base_element[4] =
7687   {
7688     EL_CONVEYOR_BELT_1_LEFT,
7689     EL_CONVEYOR_BELT_2_LEFT,
7690     EL_CONVEYOR_BELT_3_LEFT,
7691     EL_CONVEYOR_BELT_4_LEFT
7692   };
7693
7694   int belt_nr = getBeltNrFromBeltElement(element);
7695   int belt_dir_nr = element - belt_base_element[belt_nr];
7696
7697   return (belt_dir_nr % 3);
7698 }
7699
7700 int getBeltDirNrFromBeltSwitchElement(int element)
7701 {
7702   static int belt_base_element[4] =
7703   {
7704     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7705     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7706     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7707     EL_CONVEYOR_BELT_4_SWITCH_LEFT
7708   };
7709
7710   int belt_nr = getBeltNrFromBeltSwitchElement(element);
7711   int belt_dir_nr = element - belt_base_element[belt_nr];
7712
7713   return (belt_dir_nr % 3);
7714 }
7715
7716 int getBeltDirFromBeltElement(int element)
7717 {
7718   static int belt_move_dir[3] =
7719   {
7720     MV_LEFT,
7721     MV_NONE,
7722     MV_RIGHT
7723   };
7724
7725   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7726
7727   return belt_move_dir[belt_dir_nr];
7728 }
7729
7730 int getBeltDirFromBeltSwitchElement(int element)
7731 {
7732   static int belt_move_dir[3] =
7733   {
7734     MV_LEFT,
7735     MV_NONE,
7736     MV_RIGHT
7737   };
7738
7739   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7740
7741   return belt_move_dir[belt_dir_nr];
7742 }
7743
7744 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7745 {
7746   static int belt_base_element[4] =
7747   {
7748     EL_CONVEYOR_BELT_1_LEFT,
7749     EL_CONVEYOR_BELT_2_LEFT,
7750     EL_CONVEYOR_BELT_3_LEFT,
7751     EL_CONVEYOR_BELT_4_LEFT
7752   };
7753
7754   return belt_base_element[belt_nr] + belt_dir_nr;
7755 }
7756
7757 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7758 {
7759   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7760
7761   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7762 }
7763
7764 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7765 {
7766   static int belt_base_element[4] =
7767   {
7768     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7769     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7770     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7771     EL_CONVEYOR_BELT_4_SWITCH_LEFT
7772   };
7773
7774   return belt_base_element[belt_nr] + belt_dir_nr;
7775 }
7776
7777 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7778 {
7779   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7780
7781   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7782 }
7783
7784 boolean getTeamMode_EM()
7785 {
7786   return game.team_mode;
7787 }
7788
7789 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7790 {
7791   int game_frame_delay_value;
7792
7793   game_frame_delay_value =
7794     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7795      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7796      GameFrameDelay);
7797
7798   if (tape.playing && tape.warp_forward && !tape.pausing)
7799     game_frame_delay_value = 0;
7800
7801   return game_frame_delay_value;
7802 }
7803
7804 unsigned int InitRND(int seed)
7805 {
7806   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7807     return InitEngineRandom_EM(seed);
7808   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7809     return InitEngineRandom_SP(seed);
7810   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7811     return InitEngineRandom_MM(seed);
7812   else
7813     return InitEngineRandom_RND(seed);
7814 }
7815
7816 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7817 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7818
7819 inline static int get_effective_element_EM(int tile, int frame_em)
7820 {
7821   int element             = object_mapping[tile].element_rnd;
7822   int action              = object_mapping[tile].action;
7823   boolean is_backside     = object_mapping[tile].is_backside;
7824   boolean action_removing = (action == ACTION_DIGGING ||
7825                              action == ACTION_SNAPPING ||
7826                              action == ACTION_COLLECTING);
7827
7828   if (frame_em < 7)
7829   {
7830     switch (tile)
7831     {
7832       case Yacid_splash_eB:
7833       case Yacid_splash_wB:
7834         return (frame_em > 5 ? EL_EMPTY : element);
7835
7836       default:
7837         return element;
7838     }
7839   }
7840   else  /* frame_em == 7 */
7841   {
7842     switch (tile)
7843     {
7844       case Yacid_splash_eB:
7845       case Yacid_splash_wB:
7846         return EL_EMPTY;
7847
7848       case Yemerald_stone:
7849         return EL_EMERALD;
7850
7851       case Ydiamond_stone:
7852         return EL_ROCK;
7853
7854       case Xdrip_stretch:
7855       case Xdrip_stretchB:
7856       case Ydrip_s1:
7857       case Ydrip_s1B:
7858       case Xball_1B:
7859       case Xball_2:
7860       case Xball_2B:
7861       case Yball_eat:
7862       case Ykey_1_eat:
7863       case Ykey_2_eat:
7864       case Ykey_3_eat:
7865       case Ykey_4_eat:
7866       case Ykey_5_eat:
7867       case Ykey_6_eat:
7868       case Ykey_7_eat:
7869       case Ykey_8_eat:
7870       case Ylenses_eat:
7871       case Ymagnify_eat:
7872       case Ygrass_eat:
7873       case Ydirt_eat:
7874       case Xsand_stonein_1:
7875       case Xsand_stonein_2:
7876       case Xsand_stonein_3:
7877       case Xsand_stonein_4:
7878         return element;
7879
7880       default:
7881         return (is_backside || action_removing ? EL_EMPTY : element);
7882     }
7883   }
7884 }
7885
7886 inline static boolean check_linear_animation_EM(int tile)
7887 {
7888   switch (tile)
7889   {
7890     case Xsand_stonesand_1:
7891     case Xsand_stonesand_quickout_1:
7892     case Xsand_sandstone_1:
7893     case Xsand_stonein_1:
7894     case Xsand_stoneout_1:
7895     case Xboom_1:
7896     case Xdynamite_1:
7897     case Ybug_w_n:
7898     case Ybug_n_e:
7899     case Ybug_e_s:
7900     case Ybug_s_w:
7901     case Ybug_e_n:
7902     case Ybug_s_e:
7903     case Ybug_w_s:
7904     case Ybug_n_w:
7905     case Ytank_w_n:
7906     case Ytank_n_e:
7907     case Ytank_e_s:
7908     case Ytank_s_w:
7909     case Ytank_e_n:
7910     case Ytank_s_e:
7911     case Ytank_w_s:
7912     case Ytank_n_w:
7913     case Yacid_splash_eB:
7914     case Yacid_splash_wB:
7915     case Yemerald_stone:
7916       return TRUE;
7917   }
7918
7919   return FALSE;
7920 }
7921
7922 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7923                                             boolean has_crumbled_graphics,
7924                                             int crumbled, int sync_frame)
7925 {
7926   /* if element can be crumbled, but certain action graphics are just empty
7927      space (like instantly snapping sand to empty space in 1 frame), do not
7928      treat these empty space graphics as crumbled graphics in EMC engine */
7929   if (crumbled == IMG_EMPTY_SPACE)
7930     has_crumbled_graphics = FALSE;
7931
7932   if (has_crumbled_graphics)
7933   {
7934     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7935     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7936                                            g_crumbled->anim_delay,
7937                                            g_crumbled->anim_mode,
7938                                            g_crumbled->anim_start_frame,
7939                                            sync_frame);
7940
7941     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7942                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7943
7944     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7945     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7946
7947     g_em->has_crumbled_graphics = TRUE;
7948   }
7949   else
7950   {
7951     g_em->crumbled_bitmap = NULL;
7952     g_em->crumbled_src_x = 0;
7953     g_em->crumbled_src_y = 0;
7954     g_em->crumbled_border_size = 0;
7955     g_em->crumbled_tile_size = 0;
7956
7957     g_em->has_crumbled_graphics = FALSE;
7958   }
7959 }
7960
7961 void ResetGfxAnimation_EM(int x, int y, int tile)
7962 {
7963   GfxFrame[x][y] = 0;
7964 }
7965
7966 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7967                         int tile, int frame_em, int x, int y)
7968 {
7969   int action = object_mapping[tile].action;
7970   int direction = object_mapping[tile].direction;
7971   int effective_element = get_effective_element_EM(tile, frame_em);
7972   int graphic = (direction == MV_NONE ?
7973                  el_act2img(effective_element, action) :
7974                  el_act_dir2img(effective_element, action, direction));
7975   struct GraphicInfo *g = &graphic_info[graphic];
7976   int sync_frame;
7977   boolean action_removing = (action == ACTION_DIGGING ||
7978                              action == ACTION_SNAPPING ||
7979                              action == ACTION_COLLECTING);
7980   boolean action_moving   = (action == ACTION_FALLING ||
7981                              action == ACTION_MOVING ||
7982                              action == ACTION_PUSHING ||
7983                              action == ACTION_EATING ||
7984                              action == ACTION_FILLING ||
7985                              action == ACTION_EMPTYING);
7986   boolean action_falling  = (action == ACTION_FALLING ||
7987                              action == ACTION_FILLING ||
7988                              action == ACTION_EMPTYING);
7989
7990   /* special case: graphic uses "2nd movement tile" and has defined
7991      7 frames for movement animation (or less) => use default graphic
7992      for last (8th) frame which ends the movement animation */
7993   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7994   {
7995     action = ACTION_DEFAULT;    /* (keep action_* unchanged for now) */
7996     graphic = (direction == MV_NONE ?
7997                el_act2img(effective_element, action) :
7998                el_act_dir2img(effective_element, action, direction));
7999
8000     g = &graphic_info[graphic];
8001   }
8002
8003   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8004   {
8005     GfxFrame[x][y] = 0;
8006   }
8007   else if (action_moving)
8008   {
8009     boolean is_backside = object_mapping[tile].is_backside;
8010
8011     if (is_backside)
8012     {
8013       int direction = object_mapping[tile].direction;
8014       int move_dir = (action_falling ? MV_DOWN : direction);
8015
8016       GfxFrame[x][y]++;
8017
8018 #if 1
8019       /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8020       if (g->double_movement && frame_em == 0)
8021         GfxFrame[x][y] = 0;
8022 #endif
8023
8024       if (move_dir == MV_LEFT)
8025         GfxFrame[x - 1][y] = GfxFrame[x][y];
8026       else if (move_dir == MV_RIGHT)
8027         GfxFrame[x + 1][y] = GfxFrame[x][y];
8028       else if (move_dir == MV_UP)
8029         GfxFrame[x][y - 1] = GfxFrame[x][y];
8030       else if (move_dir == MV_DOWN)
8031         GfxFrame[x][y + 1] = GfxFrame[x][y];
8032     }
8033   }
8034   else
8035   {
8036     GfxFrame[x][y]++;
8037
8038     /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8039     if (tile == Xsand_stonesand_quickout_1 ||
8040         tile == Xsand_stonesand_quickout_2)
8041       GfxFrame[x][y]++;
8042   }
8043
8044   if (graphic_info[graphic].anim_global_sync)
8045     sync_frame = FrameCounter;
8046   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8047     sync_frame = GfxFrame[x][y];
8048   else
8049     sync_frame = 0;     /* playfield border (pseudo steel) */
8050
8051   SetRandomAnimationValue(x, y);
8052
8053   int frame = getAnimationFrame(g->anim_frames,
8054                                 g->anim_delay,
8055                                 g->anim_mode,
8056                                 g->anim_start_frame,
8057                                 sync_frame);
8058
8059   g_em->unique_identifier =
8060     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8061 }
8062
8063 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8064                                   int tile, int frame_em, int x, int y)
8065 {
8066   int action = object_mapping[tile].action;
8067   int direction = object_mapping[tile].direction;
8068   boolean is_backside = object_mapping[tile].is_backside;
8069   int effective_element = get_effective_element_EM(tile, frame_em);
8070   int effective_action = action;
8071   int graphic = (direction == MV_NONE ?
8072                  el_act2img(effective_element, effective_action) :
8073                  el_act_dir2img(effective_element, effective_action,
8074                                 direction));
8075   int crumbled = (direction == MV_NONE ?
8076                   el_act2crm(effective_element, effective_action) :
8077                   el_act_dir2crm(effective_element, effective_action,
8078                                  direction));
8079   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8080   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8081   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8082   struct GraphicInfo *g = &graphic_info[graphic];
8083   int sync_frame;
8084
8085   /* special case: graphic uses "2nd movement tile" and has defined
8086      7 frames for movement animation (or less) => use default graphic
8087      for last (8th) frame which ends the movement animation */
8088   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8089   {
8090     effective_action = ACTION_DEFAULT;
8091     graphic = (direction == MV_NONE ?
8092                el_act2img(effective_element, effective_action) :
8093                el_act_dir2img(effective_element, effective_action,
8094                               direction));
8095     crumbled = (direction == MV_NONE ?
8096                 el_act2crm(effective_element, effective_action) :
8097                 el_act_dir2crm(effective_element, effective_action,
8098                                direction));
8099
8100     g = &graphic_info[graphic];
8101   }
8102
8103   if (graphic_info[graphic].anim_global_sync)
8104     sync_frame = FrameCounter;
8105   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8106     sync_frame = GfxFrame[x][y];
8107   else
8108     sync_frame = 0;     /* playfield border (pseudo steel) */
8109
8110   SetRandomAnimationValue(x, y);
8111
8112   int frame = getAnimationFrame(g->anim_frames,
8113                                 g->anim_delay,
8114                                 g->anim_mode,
8115                                 g->anim_start_frame,
8116                                 sync_frame);
8117
8118   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8119                       g->double_movement && is_backside);
8120
8121   /* (updating the "crumbled" graphic definitions is probably not really needed,
8122      as animations for crumbled graphics can't be longer than one EMC cycle) */
8123   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8124                            sync_frame);
8125 }
8126
8127 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8128                                   int player_nr, int anim, int frame_em)
8129 {
8130   int element   = player_mapping[player_nr][anim].element_rnd;
8131   int action    = player_mapping[player_nr][anim].action;
8132   int direction = player_mapping[player_nr][anim].direction;
8133   int graphic = (direction == MV_NONE ?
8134                  el_act2img(element, action) :
8135                  el_act_dir2img(element, action, direction));
8136   struct GraphicInfo *g = &graphic_info[graphic];
8137   int sync_frame;
8138
8139   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8140
8141   stored_player[player_nr].StepFrame = frame_em;
8142
8143   sync_frame = stored_player[player_nr].Frame;
8144
8145   int frame = getAnimationFrame(g->anim_frames,
8146                                 g->anim_delay,
8147                                 g->anim_mode,
8148                                 g->anim_start_frame,
8149                                 sync_frame);
8150
8151   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8152                       &g_em->src_x, &g_em->src_y, FALSE);
8153 }
8154
8155 void InitGraphicInfo_EM(void)
8156 {
8157   int i, j, p;
8158
8159 #if DEBUG_EM_GFX
8160   int num_em_gfx_errors = 0;
8161
8162   if (graphic_info_em_object[0][0].bitmap == NULL)
8163   {
8164     /* EM graphics not yet initialized in em_open_all() */
8165
8166     return;
8167   }
8168
8169   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8170 #endif
8171
8172   /* always start with reliable default values */
8173   for (i = 0; i < TILE_MAX; i++)
8174   {
8175     object_mapping[i].element_rnd = EL_UNKNOWN;
8176     object_mapping[i].is_backside = FALSE;
8177     object_mapping[i].action = ACTION_DEFAULT;
8178     object_mapping[i].direction = MV_NONE;
8179   }
8180
8181   /* always start with reliable default values */
8182   for (p = 0; p < MAX_PLAYERS; p++)
8183   {
8184     for (i = 0; i < SPR_MAX; i++)
8185     {
8186       player_mapping[p][i].element_rnd = EL_UNKNOWN;
8187       player_mapping[p][i].action = ACTION_DEFAULT;
8188       player_mapping[p][i].direction = MV_NONE;
8189     }
8190   }
8191
8192   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8193   {
8194     int e = em_object_mapping_list[i].element_em;
8195
8196     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8197     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8198
8199     if (em_object_mapping_list[i].action != -1)
8200       object_mapping[e].action = em_object_mapping_list[i].action;
8201
8202     if (em_object_mapping_list[i].direction != -1)
8203       object_mapping[e].direction =
8204         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8205   }
8206
8207   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8208   {
8209     int a = em_player_mapping_list[i].action_em;
8210     int p = em_player_mapping_list[i].player_nr;
8211
8212     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8213
8214     if (em_player_mapping_list[i].action != -1)
8215       player_mapping[p][a].action = em_player_mapping_list[i].action;
8216
8217     if (em_player_mapping_list[i].direction != -1)
8218       player_mapping[p][a].direction =
8219         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8220   }
8221
8222   for (i = 0; i < TILE_MAX; i++)
8223   {
8224     int element = object_mapping[i].element_rnd;
8225     int action = object_mapping[i].action;
8226     int direction = object_mapping[i].direction;
8227     boolean is_backside = object_mapping[i].is_backside;
8228     boolean action_exploding = ((action == ACTION_EXPLODING ||
8229                                  action == ACTION_SMASHED_BY_ROCK ||
8230                                  action == ACTION_SMASHED_BY_SPRING) &&
8231                                 element != EL_DIAMOND);
8232     boolean action_active = (action == ACTION_ACTIVE);
8233     boolean action_other = (action == ACTION_OTHER);
8234
8235     for (j = 0; j < 8; j++)
8236     {
8237       int effective_element = get_effective_element_EM(i, j);
8238       int effective_action = (j < 7 ? action :
8239                               i == Xdrip_stretch ? action :
8240                               i == Xdrip_stretchB ? action :
8241                               i == Ydrip_s1 ? action :
8242                               i == Ydrip_s1B ? action :
8243                               i == Xball_1B ? action :
8244                               i == Xball_2 ? action :
8245                               i == Xball_2B ? action :
8246                               i == Yball_eat ? action :
8247                               i == Ykey_1_eat ? action :
8248                               i == Ykey_2_eat ? action :
8249                               i == Ykey_3_eat ? action :
8250                               i == Ykey_4_eat ? action :
8251                               i == Ykey_5_eat ? action :
8252                               i == Ykey_6_eat ? action :
8253                               i == Ykey_7_eat ? action :
8254                               i == Ykey_8_eat ? action :
8255                               i == Ylenses_eat ? action :
8256                               i == Ymagnify_eat ? action :
8257                               i == Ygrass_eat ? action :
8258                               i == Ydirt_eat ? action :
8259                               i == Xsand_stonein_1 ? action :
8260                               i == Xsand_stonein_2 ? action :
8261                               i == Xsand_stonein_3 ? action :
8262                               i == Xsand_stonein_4 ? action :
8263                               i == Xsand_stoneout_1 ? action :
8264                               i == Xsand_stoneout_2 ? action :
8265                               i == Xboom_android ? ACTION_EXPLODING :
8266                               action_exploding ? ACTION_EXPLODING :
8267                               action_active ? action :
8268                               action_other ? action :
8269                               ACTION_DEFAULT);
8270       int graphic = (el_act_dir2img(effective_element, effective_action,
8271                                     direction));
8272       int crumbled = (el_act_dir2crm(effective_element, effective_action,
8273                                      direction));
8274       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8275       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8276       boolean has_action_graphics = (graphic != base_graphic);
8277       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8278       struct GraphicInfo *g = &graphic_info[graphic];
8279       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8280       Bitmap *src_bitmap;
8281       int src_x, src_y;
8282       /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8283       boolean special_animation = (action != ACTION_DEFAULT &&
8284                                    g->anim_frames == 3 &&
8285                                    g->anim_delay == 2 &&
8286                                    g->anim_mode & ANIM_LINEAR);
8287       int sync_frame = (i == Xdrip_stretch ? 7 :
8288                         i == Xdrip_stretchB ? 7 :
8289                         i == Ydrip_s2 ? j + 8 :
8290                         i == Ydrip_s2B ? j + 8 :
8291                         i == Xacid_1 ? 0 :
8292                         i == Xacid_2 ? 10 :
8293                         i == Xacid_3 ? 20 :
8294                         i == Xacid_4 ? 30 :
8295                         i == Xacid_5 ? 40 :
8296                         i == Xacid_6 ? 50 :
8297                         i == Xacid_7 ? 60 :
8298                         i == Xacid_8 ? 70 :
8299                         i == Xfake_acid_1 ? 0 :
8300                         i == Xfake_acid_2 ? 10 :
8301                         i == Xfake_acid_3 ? 20 :
8302                         i == Xfake_acid_4 ? 30 :
8303                         i == Xfake_acid_5 ? 40 :
8304                         i == Xfake_acid_6 ? 50 :
8305                         i == Xfake_acid_7 ? 60 :
8306                         i == Xfake_acid_8 ? 70 :
8307                         i == Xball_2 ? 7 :
8308                         i == Xball_2B ? j + 8 :
8309                         i == Yball_eat ? j + 1 :
8310                         i == Ykey_1_eat ? j + 1 :
8311                         i == Ykey_2_eat ? j + 1 :
8312                         i == Ykey_3_eat ? j + 1 :
8313                         i == Ykey_4_eat ? j + 1 :
8314                         i == Ykey_5_eat ? j + 1 :
8315                         i == Ykey_6_eat ? j + 1 :
8316                         i == Ykey_7_eat ? j + 1 :
8317                         i == Ykey_8_eat ? j + 1 :
8318                         i == Ylenses_eat ? j + 1 :
8319                         i == Ymagnify_eat ? j + 1 :
8320                         i == Ygrass_eat ? j + 1 :
8321                         i == Ydirt_eat ? j + 1 :
8322                         i == Xamoeba_1 ? 0 :
8323                         i == Xamoeba_2 ? 1 :
8324                         i == Xamoeba_3 ? 2 :
8325                         i == Xamoeba_4 ? 3 :
8326                         i == Xamoeba_5 ? 0 :
8327                         i == Xamoeba_6 ? 1 :
8328                         i == Xamoeba_7 ? 2 :
8329                         i == Xamoeba_8 ? 3 :
8330                         i == Xexit_2 ? j + 8 :
8331                         i == Xexit_3 ? j + 16 :
8332                         i == Xdynamite_1 ? 0 :
8333                         i == Xdynamite_2 ? 8 :
8334                         i == Xdynamite_3 ? 16 :
8335                         i == Xdynamite_4 ? 24 :
8336                         i == Xsand_stonein_1 ? j + 1 :
8337                         i == Xsand_stonein_2 ? j + 9 :
8338                         i == Xsand_stonein_3 ? j + 17 :
8339                         i == Xsand_stonein_4 ? j + 25 :
8340                         i == Xsand_stoneout_1 && j == 0 ? 0 :
8341                         i == Xsand_stoneout_1 && j == 1 ? 0 :
8342                         i == Xsand_stoneout_1 && j == 2 ? 1 :
8343                         i == Xsand_stoneout_1 && j == 3 ? 2 :
8344                         i == Xsand_stoneout_1 && j == 4 ? 2 :
8345                         i == Xsand_stoneout_1 && j == 5 ? 3 :
8346                         i == Xsand_stoneout_1 && j == 6 ? 4 :
8347                         i == Xsand_stoneout_1 && j == 7 ? 4 :
8348                         i == Xsand_stoneout_2 && j == 0 ? 5 :
8349                         i == Xsand_stoneout_2 && j == 1 ? 6 :
8350                         i == Xsand_stoneout_2 && j == 2 ? 7 :
8351                         i == Xsand_stoneout_2 && j == 3 ? 8 :
8352                         i == Xsand_stoneout_2 && j == 4 ? 9 :
8353                         i == Xsand_stoneout_2 && j == 5 ? 11 :
8354                         i == Xsand_stoneout_2 && j == 6 ? 13 :
8355                         i == Xsand_stoneout_2 && j == 7 ? 15 :
8356                         i == Xboom_bug && j == 1 ? 2 :
8357                         i == Xboom_bug && j == 2 ? 2 :
8358                         i == Xboom_bug && j == 3 ? 4 :
8359                         i == Xboom_bug && j == 4 ? 4 :
8360                         i == Xboom_bug && j == 5 ? 2 :
8361                         i == Xboom_bug && j == 6 ? 2 :
8362                         i == Xboom_bug && j == 7 ? 0 :
8363                         i == Xboom_bomb && j == 1 ? 2 :
8364                         i == Xboom_bomb && j == 2 ? 2 :
8365                         i == Xboom_bomb && j == 3 ? 4 :
8366                         i == Xboom_bomb && j == 4 ? 4 :
8367                         i == Xboom_bomb && j == 5 ? 2 :
8368                         i == Xboom_bomb && j == 6 ? 2 :
8369                         i == Xboom_bomb && j == 7 ? 0 :
8370                         i == Xboom_android && j == 7 ? 6 :
8371                         i == Xboom_1 && j == 1 ? 2 :
8372                         i == Xboom_1 && j == 2 ? 2 :
8373                         i == Xboom_1 && j == 3 ? 4 :
8374                         i == Xboom_1 && j == 4 ? 4 :
8375                         i == Xboom_1 && j == 5 ? 6 :
8376                         i == Xboom_1 && j == 6 ? 6 :
8377                         i == Xboom_1 && j == 7 ? 8 :
8378                         i == Xboom_2 && j == 0 ? 8 :
8379                         i == Xboom_2 && j == 1 ? 8 :
8380                         i == Xboom_2 && j == 2 ? 10 :
8381                         i == Xboom_2 && j == 3 ? 10 :
8382                         i == Xboom_2 && j == 4 ? 10 :
8383                         i == Xboom_2 && j == 5 ? 12 :
8384                         i == Xboom_2 && j == 6 ? 12 :
8385                         i == Xboom_2 && j == 7 ? 12 :
8386                         special_animation && j == 4 ? 3 :
8387                         effective_action != action ? 0 :
8388                         j);
8389
8390 #if DEBUG_EM_GFX
8391       Bitmap *debug_bitmap = g_em->bitmap;
8392       int debug_src_x = g_em->src_x;
8393       int debug_src_y = g_em->src_y;
8394 #endif
8395
8396       int frame = getAnimationFrame(g->anim_frames,
8397                                     g->anim_delay,
8398                                     g->anim_mode,
8399                                     g->anim_start_frame,
8400                                     sync_frame);
8401
8402       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8403                           g->double_movement && is_backside);
8404
8405       g_em->bitmap = src_bitmap;
8406       g_em->src_x = src_x;
8407       g_em->src_y = src_y;
8408       g_em->src_offset_x = 0;
8409       g_em->src_offset_y = 0;
8410       g_em->dst_offset_x = 0;
8411       g_em->dst_offset_y = 0;
8412       g_em->width  = TILEX;
8413       g_em->height = TILEY;
8414
8415       g_em->preserve_background = FALSE;
8416
8417       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8418                                sync_frame);
8419
8420       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8421                                    effective_action == ACTION_MOVING  ||
8422                                    effective_action == ACTION_PUSHING ||
8423                                    effective_action == ACTION_EATING)) ||
8424           (!has_action_graphics && (effective_action == ACTION_FILLING ||
8425                                     effective_action == ACTION_EMPTYING)))
8426       {
8427         int move_dir =
8428           (effective_action == ACTION_FALLING ||
8429            effective_action == ACTION_FILLING ||
8430            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8431         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8432         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
8433         int num_steps = (i == Ydrip_s1  ? 16 :
8434                          i == Ydrip_s1B ? 16 :
8435                          i == Ydrip_s2  ? 16 :
8436                          i == Ydrip_s2B ? 16 :
8437                          i == Xsand_stonein_1 ? 32 :
8438                          i == Xsand_stonein_2 ? 32 :
8439                          i == Xsand_stonein_3 ? 32 :
8440                          i == Xsand_stonein_4 ? 32 :
8441                          i == Xsand_stoneout_1 ? 16 :
8442                          i == Xsand_stoneout_2 ? 16 : 8);
8443         int cx = ABS(dx) * (TILEX / num_steps);
8444         int cy = ABS(dy) * (TILEY / num_steps);
8445         int step_frame = (i == Ydrip_s2         ? j + 8 :
8446                           i == Ydrip_s2B        ? j + 8 :
8447                           i == Xsand_stonein_2  ? j + 8 :
8448                           i == Xsand_stonein_3  ? j + 16 :
8449                           i == Xsand_stonein_4  ? j + 24 :
8450                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8451         int step = (is_backside ? step_frame : num_steps - step_frame);
8452
8453         if (is_backside)        /* tile where movement starts */
8454         {
8455           if (dx < 0 || dy < 0)
8456           {
8457             g_em->src_offset_x = cx * step;
8458             g_em->src_offset_y = cy * step;
8459           }
8460           else
8461           {
8462             g_em->dst_offset_x = cx * step;
8463             g_em->dst_offset_y = cy * step;
8464           }
8465         }
8466         else                    /* tile where movement ends */
8467         {
8468           if (dx < 0 || dy < 0)
8469           {
8470             g_em->dst_offset_x = cx * step;
8471             g_em->dst_offset_y = cy * step;
8472           }
8473           else
8474           {
8475             g_em->src_offset_x = cx * step;
8476             g_em->src_offset_y = cy * step;
8477           }
8478         }
8479
8480         g_em->width  = TILEX - cx * step;
8481         g_em->height = TILEY - cy * step;
8482       }
8483
8484       /* create unique graphic identifier to decide if tile must be redrawn */
8485       /* bit 31 - 16 (16 bit): EM style graphic
8486          bit 15 - 12 ( 4 bit): EM style frame
8487          bit 11 -  6 ( 6 bit): graphic width
8488          bit  5 -  0 ( 6 bit): graphic height */
8489       g_em->unique_identifier =
8490         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8491
8492 #if DEBUG_EM_GFX
8493
8494       /* skip check for EMC elements not contained in original EMC artwork */
8495       if (element == EL_EMC_FAKE_ACID)
8496         continue;
8497
8498       if (g_em->bitmap != debug_bitmap ||
8499           g_em->src_x != debug_src_x ||
8500           g_em->src_y != debug_src_y ||
8501           g_em->src_offset_x != 0 ||
8502           g_em->src_offset_y != 0 ||
8503           g_em->dst_offset_x != 0 ||
8504           g_em->dst_offset_y != 0 ||
8505           g_em->width != TILEX ||
8506           g_em->height != TILEY)
8507       {
8508         static int last_i = -1;
8509
8510         if (i != last_i)
8511         {
8512           printf("\n");
8513           last_i = i;
8514         }
8515
8516         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8517                i, element, element_info[element].token_name,
8518                element_action_info[effective_action].suffix, direction);
8519
8520         if (element != effective_element)
8521           printf(" [%d ('%s')]",
8522                  effective_element,
8523                  element_info[effective_element].token_name);
8524
8525         printf("\n");
8526
8527         if (g_em->bitmap != debug_bitmap)
8528           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8529                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8530
8531         if (g_em->src_x != debug_src_x ||
8532             g_em->src_y != debug_src_y)
8533           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8534                  j, (is_backside ? 'B' : 'F'),
8535                  g_em->src_x, g_em->src_y,
8536                  g_em->src_x / 32, g_em->src_y / 32,
8537                  debug_src_x, debug_src_y,
8538                  debug_src_x / 32, debug_src_y / 32);
8539
8540         if (g_em->src_offset_x != 0 ||
8541             g_em->src_offset_y != 0 ||
8542             g_em->dst_offset_x != 0 ||
8543             g_em->dst_offset_y != 0)
8544           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8545                  j, is_backside,
8546                  g_em->src_offset_x, g_em->src_offset_y,
8547                  g_em->dst_offset_x, g_em->dst_offset_y);
8548
8549         if (g_em->width != TILEX ||
8550             g_em->height != TILEY)
8551           printf("    %d (%d): size %d,%d should be %d,%d\n",
8552                  j, is_backside,
8553                  g_em->width, g_em->height, TILEX, TILEY);
8554
8555         num_em_gfx_errors++;
8556       }
8557 #endif
8558
8559     }
8560   }
8561
8562   for (i = 0; i < TILE_MAX; i++)
8563   {
8564     for (j = 0; j < 8; j++)
8565     {
8566       int element = object_mapping[i].element_rnd;
8567       int action = object_mapping[i].action;
8568       int direction = object_mapping[i].direction;
8569       boolean is_backside = object_mapping[i].is_backside;
8570       int graphic_action  = el_act_dir2img(element, action, direction);
8571       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8572
8573       if ((action == ACTION_SMASHED_BY_ROCK ||
8574            action == ACTION_SMASHED_BY_SPRING ||
8575            action == ACTION_EATING) &&
8576           graphic_action == graphic_default)
8577       {
8578         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
8579                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8580                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
8581                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8582                  Xspring);
8583
8584         /* no separate animation for "smashed by rock" -- use rock instead */
8585         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8586         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8587
8588         g_em->bitmap            = g_xx->bitmap;
8589         g_em->src_x             = g_xx->src_x;
8590         g_em->src_y             = g_xx->src_y;
8591         g_em->src_offset_x      = g_xx->src_offset_x;
8592         g_em->src_offset_y      = g_xx->src_offset_y;
8593         g_em->dst_offset_x      = g_xx->dst_offset_x;
8594         g_em->dst_offset_y      = g_xx->dst_offset_y;
8595         g_em->width             = g_xx->width;
8596         g_em->height            = g_xx->height;
8597         g_em->unique_identifier = g_xx->unique_identifier;
8598
8599         if (!is_backside)
8600           g_em->preserve_background = TRUE;
8601       }
8602     }
8603   }
8604
8605   for (p = 0; p < MAX_PLAYERS; p++)
8606   {
8607     for (i = 0; i < SPR_MAX; i++)
8608     {
8609       int element = player_mapping[p][i].element_rnd;
8610       int action = player_mapping[p][i].action;
8611       int direction = player_mapping[p][i].direction;
8612
8613       for (j = 0; j < 8; j++)
8614       {
8615         int effective_element = element;
8616         int effective_action = action;
8617         int graphic = (direction == MV_NONE ?
8618                        el_act2img(effective_element, effective_action) :
8619                        el_act_dir2img(effective_element, effective_action,
8620                                       direction));
8621         struct GraphicInfo *g = &graphic_info[graphic];
8622         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8623         Bitmap *src_bitmap;
8624         int src_x, src_y;
8625         int sync_frame = j;
8626
8627 #if DEBUG_EM_GFX
8628         Bitmap *debug_bitmap = g_em->bitmap;
8629         int debug_src_x = g_em->src_x;
8630         int debug_src_y = g_em->src_y;
8631 #endif
8632
8633         int frame = getAnimationFrame(g->anim_frames,
8634                                       g->anim_delay,
8635                                       g->anim_mode,
8636                                       g->anim_start_frame,
8637                                       sync_frame);
8638
8639         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8640
8641         g_em->bitmap = src_bitmap;
8642         g_em->src_x = src_x;
8643         g_em->src_y = src_y;
8644         g_em->src_offset_x = 0;
8645         g_em->src_offset_y = 0;
8646         g_em->dst_offset_x = 0;
8647         g_em->dst_offset_y = 0;
8648         g_em->width  = TILEX;
8649         g_em->height = TILEY;
8650
8651 #if DEBUG_EM_GFX
8652
8653         /* skip check for EMC elements not contained in original EMC artwork */
8654         if (element == EL_PLAYER_3 ||
8655             element == EL_PLAYER_4)
8656           continue;
8657
8658         if (g_em->bitmap != debug_bitmap ||
8659             g_em->src_x != debug_src_x ||
8660             g_em->src_y != debug_src_y)
8661         {
8662           static int last_i = -1;
8663
8664           if (i != last_i)
8665           {
8666             printf("\n");
8667             last_i = i;
8668           }
8669
8670           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8671                  p, i, element, element_info[element].token_name,
8672                  element_action_info[effective_action].suffix, direction);
8673
8674           if (element != effective_element)
8675             printf(" [%d ('%s')]",
8676                    effective_element,
8677                    element_info[effective_element].token_name);
8678
8679           printf("\n");
8680
8681           if (g_em->bitmap != debug_bitmap)
8682             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
8683                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
8684
8685           if (g_em->src_x != debug_src_x ||
8686               g_em->src_y != debug_src_y)
8687             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8688                    j,
8689                    g_em->src_x, g_em->src_y,
8690                    g_em->src_x / 32, g_em->src_y / 32,
8691                    debug_src_x, debug_src_y,
8692                    debug_src_x / 32, debug_src_y / 32);
8693
8694           num_em_gfx_errors++;
8695         }
8696 #endif
8697
8698       }
8699     }
8700   }
8701
8702 #if DEBUG_EM_GFX
8703   printf("\n");
8704   printf("::: [%d errors found]\n", num_em_gfx_errors);
8705
8706   exit(0);
8707 #endif
8708 }
8709
8710 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8711                                 boolean any_player_moving,
8712                                 boolean any_player_snapping,
8713                                 boolean any_player_dropping)
8714 {
8715   if (frame == 0 && !any_player_dropping)
8716   {
8717     if (!local_player->was_waiting)
8718     {
8719       if (!CheckSaveEngineSnapshotToList())
8720         return;
8721
8722       local_player->was_waiting = TRUE;
8723     }
8724   }
8725   else if (any_player_moving || any_player_snapping || any_player_dropping)
8726   {
8727     local_player->was_waiting = FALSE;
8728   }
8729 }
8730
8731 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8732                                 boolean murphy_is_dropping)
8733 {
8734   if (murphy_is_waiting)
8735   {
8736     if (!local_player->was_waiting)
8737     {
8738       if (!CheckSaveEngineSnapshotToList())
8739         return;
8740
8741       local_player->was_waiting = TRUE;
8742     }
8743   }
8744   else
8745   {
8746     local_player->was_waiting = FALSE;
8747   }
8748 }
8749
8750 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
8751                                 boolean button_released)
8752 {
8753   if (button_released)
8754   {
8755     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
8756       CheckSaveEngineSnapshotToList();
8757   }
8758   else if (element_clicked)
8759   {
8760     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
8761       CheckSaveEngineSnapshotToList();
8762
8763     game.snapshot.changed_action = TRUE;
8764   }
8765 }
8766
8767 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8768                             boolean any_player_moving,
8769                             boolean any_player_snapping,
8770                             boolean any_player_dropping)
8771 {
8772   if (tape.single_step && tape.recording && !tape.pausing)
8773     if (frame == 0 && !any_player_dropping)
8774       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8775
8776   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8777                              any_player_snapping, any_player_dropping);
8778 }
8779
8780 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8781                             boolean murphy_is_dropping)
8782 {
8783   boolean murphy_starts_dropping = FALSE;
8784   int i;
8785
8786   for (i = 0; i < MAX_PLAYERS; i++)
8787     if (stored_player[i].force_dropping)
8788       murphy_starts_dropping = TRUE;
8789
8790   if (tape.single_step && tape.recording && !tape.pausing)
8791     if (murphy_is_waiting && !murphy_starts_dropping)
8792       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8793
8794   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8795 }
8796
8797 void CheckSingleStepMode_MM(boolean element_clicked,
8798                             boolean button_released)
8799 {
8800   if (tape.single_step && tape.recording && !tape.pausing)
8801     if (button_released)
8802       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8803
8804   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
8805 }
8806
8807 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8808                          int graphic, int sync_frame, int x, int y)
8809 {
8810   int frame = getGraphicAnimationFrame(graphic, sync_frame);
8811
8812   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8813 }
8814
8815 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8816 {
8817   return (IS_NEXT_FRAME(sync_frame, graphic));
8818 }
8819
8820 int getGraphicInfo_Delay(int graphic)
8821 {
8822   return graphic_info[graphic].anim_delay;
8823 }
8824
8825 void PlayMenuSoundExt(int sound)
8826 {
8827   if (sound == SND_UNDEFINED)
8828     return;
8829
8830   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8831       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8832     return;
8833
8834   if (IS_LOOP_SOUND(sound))
8835     PlaySoundLoop(sound);
8836   else
8837     PlaySound(sound);
8838 }
8839
8840 void PlayMenuSound()
8841 {
8842   PlayMenuSoundExt(menu.sound[game_status]);
8843 }
8844
8845 void PlayMenuSoundStereo(int sound, int stereo_position)
8846 {
8847   if (sound == SND_UNDEFINED)
8848     return;
8849
8850   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8851       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8852     return;
8853
8854   if (IS_LOOP_SOUND(sound))
8855     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8856   else
8857     PlaySoundStereo(sound, stereo_position);
8858 }
8859
8860 void PlayMenuSoundIfLoopExt(int sound)
8861 {
8862   if (sound == SND_UNDEFINED)
8863     return;
8864
8865   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8866       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8867     return;
8868
8869   if (IS_LOOP_SOUND(sound))
8870     PlaySoundLoop(sound);
8871 }
8872
8873 void PlayMenuSoundIfLoop()
8874 {
8875   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8876 }
8877
8878 void PlayMenuMusicExt(int music)
8879 {
8880   if (music == MUS_UNDEFINED)
8881     return;
8882
8883   if (!setup.sound_music)
8884     return;
8885
8886   PlayMusic(music);
8887 }
8888
8889 void PlayMenuMusic()
8890 {
8891   char *curr_music = getCurrentlyPlayingMusicFilename();
8892   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8893
8894   if (!strEqual(curr_music, next_music))
8895     PlayMenuMusicExt(menu.music[game_status]);
8896 }
8897
8898 void PlayMenuSoundsAndMusic()
8899 {
8900   PlayMenuSound();
8901   PlayMenuMusic();
8902 }
8903
8904 static void FadeMenuSounds()
8905 {
8906   FadeSounds();
8907 }
8908
8909 static void FadeMenuMusic()
8910 {
8911   char *curr_music = getCurrentlyPlayingMusicFilename();
8912   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8913
8914   if (!strEqual(curr_music, next_music))
8915     FadeMusic();
8916 }
8917
8918 void FadeMenuSoundsAndMusic()
8919 {
8920   FadeMenuSounds();
8921   FadeMenuMusic();
8922 }
8923
8924 void PlaySoundActivating()
8925 {
8926 #if 0
8927   PlaySound(SND_MENU_ITEM_ACTIVATING);
8928 #endif
8929 }
8930
8931 void PlaySoundSelecting()
8932 {
8933 #if 0
8934   PlaySound(SND_MENU_ITEM_SELECTING);
8935 #endif
8936 }
8937
8938 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8939 {
8940   boolean change_fullscreen = (setup.fullscreen !=
8941                                video.fullscreen_enabled);
8942   boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8943                                            setup.window_scaling_percent !=
8944                                            video.window_scaling_percent);
8945
8946   if (change_window_scaling_percent && video.fullscreen_enabled)
8947     return;
8948
8949   if (!change_window_scaling_percent && !video.fullscreen_available)
8950     return;
8951
8952 #if defined(TARGET_SDL2)
8953   if (change_window_scaling_percent)
8954   {
8955     SDLSetWindowScaling(setup.window_scaling_percent);
8956
8957     return;
8958   }
8959   else if (change_fullscreen)
8960   {
8961     SDLSetWindowFullscreen(setup.fullscreen);
8962
8963     /* set setup value according to successfully changed fullscreen mode */
8964     setup.fullscreen = video.fullscreen_enabled;
8965
8966     return;
8967   }
8968 #endif
8969
8970   if (change_fullscreen ||
8971       change_window_scaling_percent)
8972   {
8973     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8974
8975     /* save backbuffer content which gets lost when toggling fullscreen mode */
8976     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8977
8978     if (change_window_scaling_percent)
8979     {
8980       /* keep window mode, but change window scaling */
8981       video.fullscreen_enabled = TRUE;          /* force new window scaling */
8982     }
8983
8984     /* toggle fullscreen */
8985     ChangeVideoModeIfNeeded(setup.fullscreen);
8986
8987     /* set setup value according to successfully changed fullscreen mode */
8988     setup.fullscreen = video.fullscreen_enabled;
8989
8990     /* restore backbuffer content from temporary backbuffer backup bitmap */
8991     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8992
8993     FreeBitmap(tmp_backbuffer);
8994
8995     /* update visible window/screen */
8996     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8997   }
8998 }
8999
9000 void JoinRectangles(int *x, int *y, int *width, int *height,
9001                     int x2, int y2, int width2, int height2)
9002 {
9003   // do not join with "off-screen" rectangle
9004   if (x2 == -1 || y2 == -1)
9005     return;
9006
9007   *x = MIN(*x, x2);
9008   *y = MIN(*y, y2);
9009   *width = MAX(*width, width2);
9010   *height = MAX(*height, height2);
9011 }
9012
9013 void SetAnimStatus(int anim_status_new)
9014 {
9015   if (anim_status_new == GAME_MODE_MAIN)
9016     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9017   else if (anim_status_new == GAME_MODE_SCORES)
9018     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9019
9020   global.anim_status_next = anim_status_new;
9021
9022   // directly set screen modes that are entered without fading
9023   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
9024        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9025       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
9026        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9027     global.anim_status = global.anim_status_next;
9028 }
9029
9030 void SetGameStatus(int game_status_new)
9031 {
9032   if (game_status_new != game_status)
9033     game_status_last_screen = game_status;
9034
9035   game_status = game_status_new;
9036
9037   SetAnimStatus(game_status_new);
9038 }
9039
9040 void SetFontStatus(int game_status_new)
9041 {
9042   static int last_game_status = -1;
9043
9044   if (game_status_new != -1)
9045   {
9046     // set game status for font use after storing last game status
9047     last_game_status = game_status;
9048     game_status = game_status_new;
9049   }
9050   else
9051   {
9052     // reset game status after font use from last stored game status
9053     game_status = last_game_status;
9054   }
9055 }
9056
9057 void ResetFontStatus()
9058 {
9059   SetFontStatus(-1);
9060 }
9061
9062 boolean CheckIfPlayfieldViewportHasChanged()
9063 {
9064   // if game status has not changed, playfield viewport has not changed either
9065   if (game_status == game_status_last)
9066     return FALSE;
9067
9068   // check if playfield viewport has changed with current game status
9069   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9070   int new_real_sx       = vp_playfield->x;
9071   int new_real_sy       = vp_playfield->y;
9072   int new_full_sxsize   = vp_playfield->width;
9073   int new_full_sysize   = vp_playfield->height;
9074
9075   return (new_real_sx != REAL_SX ||
9076           new_real_sy != REAL_SY ||
9077           new_full_sxsize != FULL_SXSIZE ||
9078           new_full_sysize != FULL_SYSIZE);
9079 }
9080
9081 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9082 {
9083   return (CheckIfGlobalBorderHasChanged() ||
9084           CheckIfPlayfieldViewportHasChanged());
9085 }
9086
9087 void ChangeViewportPropertiesIfNeeded()
9088 {
9089   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9090                                FALSE : setup.small_game_graphics);
9091   int gfx_game_mode = game_status;
9092   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9093                         game_status);
9094   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
9095   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9096   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
9097   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
9098   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
9099   int new_win_xsize     = vp_window->width;
9100   int new_win_ysize     = vp_window->height;
9101   int border_size       = vp_playfield->border_size;
9102   int new_sx            = vp_playfield->x + border_size;
9103   int new_sy            = vp_playfield->y + border_size;
9104   int new_sxsize        = vp_playfield->width  - 2 * border_size;
9105   int new_sysize        = vp_playfield->height - 2 * border_size;
9106   int new_real_sx       = vp_playfield->x;
9107   int new_real_sy       = vp_playfield->y;
9108   int new_full_sxsize   = vp_playfield->width;
9109   int new_full_sysize   = vp_playfield->height;
9110   int new_dx            = vp_door_1->x;
9111   int new_dy            = vp_door_1->y;
9112   int new_dxsize        = vp_door_1->width;
9113   int new_dysize        = vp_door_1->height;
9114   int new_vx            = vp_door_2->x;
9115   int new_vy            = vp_door_2->y;
9116   int new_vxsize        = vp_door_2->width;
9117   int new_vysize        = vp_door_2->height;
9118   int new_ex            = vp_door_3->x;
9119   int new_ey            = vp_door_3->y;
9120   int new_exsize        = vp_door_3->width;
9121   int new_eysize        = vp_door_3->height;
9122   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9123   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9124                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9125   int new_scr_fieldx = new_sxsize / tilesize;
9126   int new_scr_fieldy = new_sysize / tilesize;
9127   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9128   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9129   boolean init_gfx_buffers = FALSE;
9130   boolean init_video_buffer = FALSE;
9131   boolean init_gadgets_and_anims = FALSE;
9132   boolean init_em_graphics = FALSE;
9133
9134   if (new_win_xsize != WIN_XSIZE ||
9135       new_win_ysize != WIN_YSIZE)
9136   {
9137     WIN_XSIZE = new_win_xsize;
9138     WIN_YSIZE = new_win_ysize;
9139
9140     init_video_buffer = TRUE;
9141     init_gfx_buffers = TRUE;
9142     init_gadgets_and_anims = TRUE;
9143
9144     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9145   }
9146
9147   if (new_scr_fieldx != SCR_FIELDX ||
9148       new_scr_fieldy != SCR_FIELDY)
9149   {
9150     /* this always toggles between MAIN and GAME when using small tile size */
9151
9152     SCR_FIELDX = new_scr_fieldx;
9153     SCR_FIELDY = new_scr_fieldy;
9154
9155     // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9156   }
9157
9158   if (new_sx != SX ||
9159       new_sy != SY ||
9160       new_dx != DX ||
9161       new_dy != DY ||
9162       new_vx != VX ||
9163       new_vy != VY ||
9164       new_ex != EX ||
9165       new_ey != EY ||
9166       new_sxsize != SXSIZE ||
9167       new_sysize != SYSIZE ||
9168       new_dxsize != DXSIZE ||
9169       new_dysize != DYSIZE ||
9170       new_vxsize != VXSIZE ||
9171       new_vysize != VYSIZE ||
9172       new_exsize != EXSIZE ||
9173       new_eysize != EYSIZE ||
9174       new_real_sx != REAL_SX ||
9175       new_real_sy != REAL_SY ||
9176       new_full_sxsize != FULL_SXSIZE ||
9177       new_full_sysize != FULL_SYSIZE ||
9178       new_tilesize_var != TILESIZE_VAR
9179       )
9180   {
9181     // ------------------------------------------------------------------------
9182     // determine next fading area for changed viewport definitions
9183     // ------------------------------------------------------------------------
9184
9185     // start with current playfield area (default fading area)
9186     FADE_SX = REAL_SX;
9187     FADE_SY = REAL_SY;
9188     FADE_SXSIZE = FULL_SXSIZE;
9189     FADE_SYSIZE = FULL_SYSIZE;
9190
9191     // add new playfield area if position or size has changed
9192     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9193         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9194     {
9195       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9196                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9197     }
9198
9199     // add current and new door 1 area if position or size has changed
9200     if (new_dx != DX || new_dy != DY ||
9201         new_dxsize != DXSIZE || new_dysize != DYSIZE)
9202     {
9203       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9204                      DX, DY, DXSIZE, DYSIZE);
9205       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9206                      new_dx, new_dy, new_dxsize, new_dysize);
9207     }
9208
9209     // add current and new door 2 area if position or size has changed
9210     if (new_dx != VX || new_dy != VY ||
9211         new_dxsize != VXSIZE || new_dysize != VYSIZE)
9212     {
9213       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9214                      VX, VY, VXSIZE, VYSIZE);
9215       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9216                      new_vx, new_vy, new_vxsize, new_vysize);
9217     }
9218
9219     // ------------------------------------------------------------------------
9220     // handle changed tile size
9221     // ------------------------------------------------------------------------
9222
9223     if (new_tilesize_var != TILESIZE_VAR)
9224     {
9225       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9226
9227       // changing tile size invalidates scroll values of engine snapshots
9228       FreeEngineSnapshotSingle();
9229
9230       // changing tile size requires update of graphic mapping for EM engine
9231       init_em_graphics = TRUE;
9232     }
9233
9234     SX = new_sx;
9235     SY = new_sy;
9236     DX = new_dx;
9237     DY = new_dy;
9238     VX = new_vx;
9239     VY = new_vy;
9240     EX = new_ex;
9241     EY = new_ey;
9242     SXSIZE = new_sxsize;
9243     SYSIZE = new_sysize;
9244     DXSIZE = new_dxsize;
9245     DYSIZE = new_dysize;
9246     VXSIZE = new_vxsize;
9247     VYSIZE = new_vysize;
9248     EXSIZE = new_exsize;
9249     EYSIZE = new_eysize;
9250     REAL_SX = new_real_sx;
9251     REAL_SY = new_real_sy;
9252     FULL_SXSIZE = new_full_sxsize;
9253     FULL_SYSIZE = new_full_sysize;
9254     TILESIZE_VAR = new_tilesize_var;
9255
9256     init_gfx_buffers = TRUE;
9257     init_gadgets_and_anims = TRUE;
9258
9259     // printf("::: viewports: init_gfx_buffers\n");
9260     // printf("::: viewports: init_gadgets_and_anims\n");
9261   }
9262
9263   if (init_gfx_buffers)
9264   {
9265     // printf("::: init_gfx_buffers\n");
9266
9267     SCR_FIELDX = new_scr_fieldx_buffers;
9268     SCR_FIELDY = new_scr_fieldy_buffers;
9269
9270     InitGfxBuffers();
9271
9272     SCR_FIELDX = new_scr_fieldx;
9273     SCR_FIELDY = new_scr_fieldy;
9274
9275     SetDrawDeactivationMask(REDRAW_NONE);
9276     SetDrawBackgroundMask(REDRAW_FIELD);
9277   }
9278
9279   if (init_video_buffer)
9280   {
9281     // printf("::: init_video_buffer\n");
9282
9283     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9284     InitImageTextures();
9285   }
9286
9287   if (init_gadgets_and_anims)
9288   {
9289     // printf("::: init_gadgets_and_anims\n");
9290
9291     InitGadgets();
9292     InitGlobalAnimations();
9293   }
9294
9295   if (init_em_graphics)
9296   {
9297       InitGraphicInfo_EM();
9298   }
9299 }