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