added pushable/movable properties for BD bubble (bladder)
[rocksndiamonds.git] / src / tools.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // tools.c
10 // ============================================================================
11
12 #include <math.h>
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "init.h"
18 #include "game.h"
19 #include "events.h"
20 #include "anim.h"
21 #include "network.h"
22 #include "tape.h"
23 #include "screens.h"
24
25
26 #define DEBUG_FRAME_TIME        FALSE
27
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES        0
30 #define TOOL_CTRL_ID_NO         1
31 #define TOOL_CTRL_ID_CONFIRM    2
32 #define TOOL_CTRL_ID_PLAYER_1   3
33 #define TOOL_CTRL_ID_PLAYER_2   4
34 #define TOOL_CTRL_ID_PLAYER_3   5
35 #define TOOL_CTRL_ID_PLAYER_4   6
36 #define TOOL_CTRL_ID_TOUCH_YES  7
37 #define TOOL_CTRL_ID_TOUCH_NO   8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
39
40 #define NUM_TOOL_BUTTONS        10
41
42 // constants for number of doors and door parts
43 #define NUM_DOORS               2
44 #define NUM_PANELS              NUM_DOORS
45 // #define NUM_PANELS           0
46 #define MAX_PARTS_PER_DOOR      8
47 #define MAX_DOOR_PARTS          (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i)   ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49
50
51 struct DoorPartOrderInfo
52 {
53   int nr;
54   int sort_priority;
55 };
56
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58
59 struct DoorPartControlInfo
60 {
61   int door_token;
62   int graphic;
63   struct DoorPartPosInfo *pos;
64 };
65
66 static struct DoorPartControlInfo door_part_controls[] =
67 {
68   {
69     DOOR_1,
70     IMG_GFX_DOOR_1_PART_1,
71     &door_1.part_1
72   },
73   {
74     DOOR_1,
75     IMG_GFX_DOOR_1_PART_2,
76     &door_1.part_2
77   },
78   {
79     DOOR_1,
80     IMG_GFX_DOOR_1_PART_3,
81     &door_1.part_3
82   },
83   {
84     DOOR_1,
85     IMG_GFX_DOOR_1_PART_4,
86     &door_1.part_4
87   },
88   {
89     DOOR_1,
90     IMG_GFX_DOOR_1_PART_5,
91     &door_1.part_5
92   },
93   {
94     DOOR_1,
95     IMG_GFX_DOOR_1_PART_6,
96     &door_1.part_6
97   },
98   {
99     DOOR_1,
100     IMG_GFX_DOOR_1_PART_7,
101     &door_1.part_7
102   },
103   {
104     DOOR_1,
105     IMG_GFX_DOOR_1_PART_8,
106     &door_1.part_8
107   },
108
109   {
110     DOOR_2,
111     IMG_GFX_DOOR_2_PART_1,
112     &door_2.part_1
113   },
114   {
115     DOOR_2,
116     IMG_GFX_DOOR_2_PART_2,
117     &door_2.part_2
118   },
119   {
120     DOOR_2,
121     IMG_GFX_DOOR_2_PART_3,
122     &door_2.part_3
123   },
124   {
125     DOOR_2,
126     IMG_GFX_DOOR_2_PART_4,
127     &door_2.part_4
128   },
129   {
130     DOOR_2,
131     IMG_GFX_DOOR_2_PART_5,
132     &door_2.part_5
133   },
134   {
135     DOOR_2,
136     IMG_GFX_DOOR_2_PART_6,
137     &door_2.part_6
138   },
139   {
140     DOOR_2,
141     IMG_GFX_DOOR_2_PART_7,
142     &door_2.part_7
143   },
144   {
145     DOOR_2,
146     IMG_GFX_DOOR_2_PART_8,
147     &door_2.part_8
148   },
149
150   {
151     DOOR_1,
152     IMG_BACKGROUND_PANEL,
153     &door_1.panel
154   },
155   {
156     DOOR_2,
157     IMG_BACKGROUND_TAPE,
158     &door_2.panel
159   },
160
161   {
162     -1,
163     -1,
164     NULL
165   }
166 };
167
168 static struct XY xy_topdown[] =
169 {
170   {  0, -1 },
171   { -1,  0 },
172   { +1,  0 },
173   {  0, +1 }
174 };
175
176
177 // forward declaration for internal use
178 static void MapToolButtons(unsigned int);
179 static void UnmapToolButtons(void);
180 static void HandleToolButtons(struct GadgetInfo *);
181 static int el_act_dir2crm(int, int, int);
182 static int el_act2crm(int, int);
183
184 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
185 static int request_gadget_id = -1;
186
187 static char *print_if_not_empty(int element)
188 {
189   static char *s = NULL;
190   char *token_name = element_info[element].token_name;
191
192   if (s != NULL)
193     free(s);
194
195   s = checked_malloc(strlen(token_name) + 10 + 1);
196
197   if (element != EL_EMPTY)
198     sprintf(s, "%d\t['%s']", element, token_name);
199   else
200     sprintf(s, "%d", element);
201
202   return s;
203 }
204
205 int getFieldbufferOffsetX_RND(int dir, int pos)
206 {
207   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
208   int dx = (dir & MV_HORIZONTAL ? pos : 0);
209   int dx_var = dx * TILESIZE_VAR / TILESIZE;
210   int fx = FX;
211
212   if (EVEN(SCR_FIELDX))
213   {
214     int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
215     int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
216
217     if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
218       fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
219     else
220       fx += (dx_var > 0 ? TILEX_VAR : 0);
221   }
222   else
223   {
224     fx += dx_var;
225   }
226
227   if (full_lev_fieldx <= SCR_FIELDX)
228   {
229     if (EVEN(SCR_FIELDX))
230       fx = 2 * TILEX_VAR - (ODD(lev_fieldx)  ? TILEX_VAR / 2 : 0);
231     else
232       fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
233   }
234
235   return fx;
236 }
237
238 int getFieldbufferOffsetY_RND(int dir, int pos)
239 {
240   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
241   int dy = (dir & MV_VERTICAL ? pos : 0);
242   int dy_var = dy * TILESIZE_VAR / TILESIZE;
243   int fy = FY;
244
245   if (EVEN(SCR_FIELDY))
246   {
247     int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
248     int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
249
250     if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
251       fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
252     else
253       fy += (dy_var > 0 ? TILEY_VAR : 0);
254   }
255   else
256   {
257     fy += dy_var;
258   }
259
260   if (full_lev_fieldy <= SCR_FIELDY)
261   {
262     if (EVEN(SCR_FIELDY))
263       fy = 2 * TILEY_VAR - (ODD(lev_fieldy)  ? TILEY_VAR / 2 : 0);
264     else
265       fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
266   }
267
268   return fy;
269 }
270
271 static int getLevelFromScreenX_RND(int sx)
272 {
273   int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
274   int dx = fx - FX;
275   int px = sx - SX;
276   int lx = LEVELX((px + dx) / TILESIZE_VAR);
277
278   return lx;
279 }
280
281 static int getLevelFromScreenY_RND(int sy)
282 {
283   int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
284   int dy = fy - FY;
285   int py = sy - SY;
286   int ly = LEVELY((py + dy) / TILESIZE_VAR);
287
288   return ly;
289 }
290
291 static int getLevelFromScreenX_EM(int sx)
292 {
293   int level_xsize = level.native_em_level->cav->width;
294   int full_xsize = level_xsize * TILESIZE_VAR;
295
296   sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
297
298   int fx = getFieldbufferOffsetX_EM();
299   int dx = fx;
300   int px = sx - SX;
301   int lx = LEVELX((px + dx) / TILESIZE_VAR);
302
303   return lx;
304 }
305
306 static int getLevelFromScreenY_EM(int sy)
307 {
308   int level_ysize = level.native_em_level->cav->height;
309   int full_ysize = level_ysize * TILESIZE_VAR;
310
311   sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
312
313   int fy = getFieldbufferOffsetY_EM();
314   int dy = fy;
315   int py = sy - SY;
316   int ly = LEVELY((py + dy) / TILESIZE_VAR);
317
318   return ly;
319 }
320
321 static int getLevelFromScreenX_SP(int sx)
322 {
323   int menBorder = setup.sp_show_border_elements;
324   int level_xsize = level.native_sp_level->width;
325   int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
326
327   sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
328
329   int fx = getFieldbufferOffsetX_SP();
330   int dx = fx - FX;
331   int px = sx - SX;
332   int lx = LEVELX((px + dx) / TILESIZE_VAR);
333
334   return lx;
335 }
336
337 static int getLevelFromScreenY_SP(int sy)
338 {
339   int menBorder = setup.sp_show_border_elements;
340   int level_ysize = level.native_sp_level->height;
341   int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
342
343   sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
344
345   int fy = getFieldbufferOffsetY_SP();
346   int dy = fy - FY;
347   int py = sy - SY;
348   int ly = LEVELY((py + dy) / TILESIZE_VAR);
349
350   return ly;
351 }
352
353 static int getLevelFromScreenX_MM(int sx)
354 {
355   int level_xsize = level.native_mm_level->fieldx;
356   int full_xsize = level_xsize * TILESIZE_VAR;
357
358   sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
359
360   int px = sx - SX;
361   int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
362
363   return lx;
364 }
365
366 static int getLevelFromScreenY_MM(int sy)
367 {
368   int level_ysize = level.native_mm_level->fieldy;
369   int full_ysize = level_ysize * TILESIZE_VAR;
370
371   sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
372
373   int py = sy - SY;
374   int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
375
376   return ly;
377 }
378
379 int getLevelFromScreenX(int x)
380 {
381   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
382     return getLevelFromScreenX_EM(x);
383   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
384     return getLevelFromScreenX_SP(x);
385   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
386     return getLevelFromScreenX_MM(x);
387   else
388     return getLevelFromScreenX_RND(x);
389 }
390
391 int getLevelFromScreenY(int y)
392 {
393   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394     return getLevelFromScreenY_EM(y);
395   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396     return getLevelFromScreenY_SP(y);
397   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398     return getLevelFromScreenY_MM(y);
399   else
400     return getLevelFromScreenY_RND(y);
401 }
402
403 int getScreenFieldSizeX(void)
404 {
405   return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
406 }
407
408 int getScreenFieldSizeY(void)
409 {
410   return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
411 }
412
413 void DumpTile(int x, int y)
414 {
415   int sx = SCREENX(x);
416   int sy = SCREENY(y);
417   char *token_name;
418
419   Info("---");
420   Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
421   Info("---");
422
423   if (!IN_LEV_FIELD(x, y))
424   {
425     Info("(not in level field)");
426     Info("");
427
428     return;
429   }
430
431   token_name = element_info[Tile[x][y]].token_name;
432
433   Info("Tile:        %d\t['%s']",       Tile[x][y], token_name);
434   Info("Back:        %s",               print_if_not_empty(Back[x][y]));
435   Info("Store:       %s",               print_if_not_empty(Store[x][y]));
436   Info("Store2:      %s",               print_if_not_empty(Store2[x][y]));
437   Info("StorePlayer: %s",               print_if_not_empty(StorePlayer[x][y]));
438   Info("MovPos:      %d",               MovPos[x][y]);
439   Info("MovDir:      %d",               MovDir[x][y]);
440   Info("MovDelay:    %d",               MovDelay[x][y]);
441   Info("ChangeDelay: %d",               ChangeDelay[x][y]);
442   Info("CustomValue: %d",               CustomValue[x][y]);
443   Info("GfxElement:  %d",               GfxElement[x][y]);
444   Info("GfxAction:   %d",               GfxAction[x][y]);
445   Info("GfxFrame:    %d [%d]",          GfxFrame[x][y], FrameCounter);
446   Info("Player x/y:  %d, %d",           local_player->jx, local_player->jy);
447   Info("");
448 }
449
450 void DumpTileFromScreen(int sx, int sy)
451 {
452   int lx = getLevelFromScreenX(sx);
453   int ly = getLevelFromScreenY(sy);
454
455   DumpTile(lx, ly);
456 }
457
458 void SetDrawtoField(int mode)
459 {
460   if (mode == DRAW_TO_FIELDBUFFER)
461   {
462     FX = 2 * TILEX_VAR;
463     FY = 2 * TILEY_VAR;
464     BX1 = -2;
465     BY1 = -2;
466     BX2 = SCR_FIELDX + 1;
467     BY2 = SCR_FIELDY + 1;
468
469     drawto_field = fieldbuffer;
470   }
471   else  // DRAW_TO_BACKBUFFER
472   {
473     FX = SX;
474     FY = SY;
475     BX1 = 0;
476     BY1 = 0;
477     BX2 = SCR_FIELDX - 1;
478     BY2 = SCR_FIELDY - 1;
479
480     drawto_field = backbuffer;
481   }
482 }
483
484 int GetDrawtoField(void)
485 {
486   return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
487 }
488
489 static void RedrawPlayfield_RND(void)
490 {
491   if (game.envelope_active)
492     return;
493
494   DrawLevel(REDRAW_ALL);
495   DrawAllPlayers();
496 }
497
498 void RedrawPlayfield(void)
499 {
500   if (game_status != GAME_MODE_PLAYING)
501     return;
502
503   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
504     RedrawPlayfield_BD(TRUE);
505   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
506     RedrawPlayfield_EM(TRUE);
507   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
508     RedrawPlayfield_SP(TRUE);
509   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
510     RedrawPlayfield_MM();
511   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
512     RedrawPlayfield_RND();
513
514   BlitScreenToBitmap(backbuffer);
515
516   BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
517              gfx.sx, gfx.sy);
518 }
519
520 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
521                                      int draw_target)
522 {
523   Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
524   Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
525
526   // may happen for "border.draw_masked.*" with undefined "global.border.*"
527   if (src_bitmap == NULL)
528     return;
529
530   if (x == -1 && y == -1)
531     return;
532
533   if (draw_target == DRAW_TO_SCREEN)
534     BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
535   else
536     BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
537 }
538
539 static void DrawMaskedBorderExt_FIELD(int draw_target)
540 {
541   if (global.border_status >= GAME_MODE_MAIN &&
542       global.border_status <= GAME_MODE_PLAYING &&
543       border.draw_masked[global.border_status])
544     DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
545                              draw_target);
546 }
547
548 static void DrawMaskedBorderExt_DOOR_1(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_1))
553     return;
554
555   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556       (global.border_status != GAME_MODE_EDITOR ||
557        border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
558     DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
559 }
560
561 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
562 {
563   // when drawing to backbuffer, never draw border over open doors
564   if (draw_target == DRAW_TO_BACKBUFFER &&
565       (GetDoorState() & DOOR_OPEN_2))
566     return;
567
568   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
569       global.border_status != GAME_MODE_EDITOR)
570     DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
571 }
572
573 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
574 {
575   // currently not available
576 }
577
578 static void DrawMaskedBorderExt_ALL(int draw_target)
579 {
580   DrawMaskedBorderExt_FIELD(draw_target);
581   DrawMaskedBorderExt_DOOR_1(draw_target);
582   DrawMaskedBorderExt_DOOR_2(draw_target);
583   DrawMaskedBorderExt_DOOR_3(draw_target);
584 }
585
586 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
587 {
588   // never draw masked screen borders on borderless screens
589   if (global.border_status == GAME_MODE_LOADING ||
590       global.border_status == GAME_MODE_TITLE)
591     return;
592
593   if (redraw_mask & REDRAW_ALL)
594     DrawMaskedBorderExt_ALL(draw_target);
595   else
596   {
597     if (redraw_mask & REDRAW_FIELD)
598       DrawMaskedBorderExt_FIELD(draw_target);
599     if (redraw_mask & REDRAW_DOOR_1)
600       DrawMaskedBorderExt_DOOR_1(draw_target);
601     if (redraw_mask & REDRAW_DOOR_2)
602       DrawMaskedBorderExt_DOOR_2(draw_target);
603     if (redraw_mask & REDRAW_DOOR_3)
604       DrawMaskedBorderExt_DOOR_3(draw_target);
605   }
606 }
607
608 void DrawMaskedBorder_FIELD(void)
609 {
610   DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
611 }
612
613 void DrawMaskedBorder(int redraw_mask)
614 {
615   DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
616 }
617
618 void DrawMaskedBorderToTarget(int draw_target)
619 {
620   if (draw_target == DRAW_TO_BACKBUFFER ||
621       draw_target == DRAW_TO_SCREEN)
622   {
623     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
624   }
625   else
626   {
627     int last_border_status = global.border_status;
628
629     if (draw_target == DRAW_TO_FADE_SOURCE)
630     {
631       global.border_status = gfx.fade_border_source_status;
632       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
633     }
634     else if (draw_target == DRAW_TO_FADE_TARGET)
635     {
636       global.border_status = gfx.fade_border_target_status;
637       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
638     }
639
640     // always use global border for PLAYING when restarting the game
641     if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
642       global.border_status = GAME_MODE_PLAYING;
643
644     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
645
646     global.border_status = last_border_status;
647     gfx.masked_border_bitmap_ptr = backbuffer;
648   }
649 }
650
651 void DrawTileCursor(int draw_target, int drawing_stage)
652 {
653   int tile_cursor_active = (game_status == GAME_MODE_PLAYING);
654
655   DrawTileCursor_MM(draw_target, drawing_stage, tile_cursor_active);
656 }
657
658 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
659 {
660   BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
661 }
662
663 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
664 {
665   int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
666   int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
667
668   BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
669 }
670
671 void BlitScreenToBitmap(Bitmap *target_bitmap)
672 {
673   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
674     BlitScreenToBitmap_BD(target_bitmap);
675   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
676     BlitScreenToBitmap_EM(target_bitmap);
677   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
678     BlitScreenToBitmap_SP(target_bitmap);
679   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
680     BlitScreenToBitmap_MM(target_bitmap);
681   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
682     BlitScreenToBitmap_RND(target_bitmap);
683
684   redraw_mask |= REDRAW_FIELD;
685 }
686
687 static void DrawFramesPerSecond(void)
688 {
689   char text[100];
690   int font_nr = FONT_TEXT_2;
691   int font_width = getFontWidth(font_nr);
692   int draw_deactivation_mask = GetDrawDeactivationMask();
693   boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
694
695   // draw FPS with leading space (needed if field buffer deactivated)
696   sprintf(text, " %04.1f fps", global.frames_per_second);
697
698   // override draw deactivation mask (required for invisible warp mode)
699   SetDrawDeactivationMask(REDRAW_NONE);
700
701   // draw opaque FPS if field buffer deactivated, else draw masked FPS
702   DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
703               font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
704
705   // set draw deactivation mask to previous value
706   SetDrawDeactivationMask(draw_deactivation_mask);
707
708   // force full-screen redraw in this frame
709   redraw_mask = REDRAW_ALL;
710 }
711
712 #if DEBUG_FRAME_TIME
713 static void PrintFrameTimeDebugging(void)
714 {
715   static unsigned int last_counter = 0;
716   unsigned int counter = Counter();
717   int diff_1 = counter - last_counter;
718   int diff_2 = diff_1 - GAME_FRAME_DELAY;
719   int diff_2_max = 20;
720   int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
721   char diff_bar[2 * diff_2_max + 5];
722   int pos = 0;
723   int i;
724
725   diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
726
727   for (i = 0; i < diff_2_max; i++)
728     diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
729                        i >= diff_2_max - diff_2_cut ? '-' : ' ');
730
731   diff_bar[pos++] = '|';
732
733   for (i = 0; i < diff_2_max; i++)
734     diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
735
736   diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
737
738   diff_bar[pos++] = '\0';
739
740   Debug("time:frame", "%06d [%02d] [%c%02d] %s",
741         counter,
742         diff_1,
743         (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
744         diff_bar);
745
746   last_counter = counter;
747 }
748 #endif
749
750 static int unifiedRedrawMask(int mask)
751 {
752   if (mask & REDRAW_ALL)
753     return REDRAW_ALL;
754
755   if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
756     return REDRAW_ALL;
757
758   return mask;
759 }
760
761 static boolean equalRedrawMasks(int mask_1, int mask_2)
762 {
763   return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
764 }
765
766 void BackToFront(void)
767 {
768   static int last_redraw_mask = REDRAW_NONE;
769
770   // force screen redraw in every frame to continue drawing global animations
771   // (but always use the last redraw mask to prevent unwanted side effects)
772   if (redraw_mask == REDRAW_NONE)
773     redraw_mask = last_redraw_mask;
774
775   last_redraw_mask = redraw_mask;
776
777 #if 1
778   // masked border now drawn immediately when blitting backbuffer to window
779 #else
780   // draw masked border to all viewports, if defined
781   DrawMaskedBorder(redraw_mask);
782 #endif
783
784   // draw frames per second (only if debug mode is enabled)
785   if (redraw_mask & REDRAW_FPS)
786     DrawFramesPerSecond();
787
788   // remove playfield redraw before potentially merging with doors redraw
789   if (DrawingDeactivated(REAL_SX, REAL_SY))
790     redraw_mask &= ~REDRAW_FIELD;
791
792   // redraw complete window if both playfield and (some) doors need redraw
793   if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
794     redraw_mask = REDRAW_ALL;
795
796   /* although redrawing the whole window would be fine for normal gameplay,
797      being able to only redraw the playfield is required for deactivating
798      certain drawing areas (mainly playfield) to work, which is needed for
799      warp-forward to be fast enough (by skipping redraw of most frames) */
800
801   if (redraw_mask & REDRAW_ALL)
802   {
803     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
804   }
805   else if (redraw_mask & REDRAW_FIELD)
806   {
807     BlitBitmap(backbuffer, window,
808                REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
809   }
810   else if (redraw_mask & REDRAW_DOORS)
811   {
812     // merge door areas to prevent calling screen redraw more than once
813     int x1 = WIN_XSIZE;
814     int y1 = WIN_YSIZE;
815     int x2 = 0;
816     int y2 = 0;
817
818     if (redraw_mask & REDRAW_DOOR_1)
819     {
820       x1 = MIN(x1, DX);
821       y1 = MIN(y1, DY);
822       x2 = MAX(x2, DX + DXSIZE);
823       y2 = MAX(y2, DY + DYSIZE);
824     }
825
826     if (redraw_mask & REDRAW_DOOR_2)
827     {
828       x1 = MIN(x1, VX);
829       y1 = MIN(y1, VY);
830       x2 = MAX(x2, VX + VXSIZE);
831       y2 = MAX(y2, VY + VYSIZE);
832     }
833
834     if (redraw_mask & REDRAW_DOOR_3)
835     {
836       x1 = MIN(x1, EX);
837       y1 = MIN(y1, EY);
838       x2 = MAX(x2, EX + EXSIZE);
839       y2 = MAX(y2, EY + EYSIZE);
840     }
841
842     // make sure that at least one pixel is blitted, and inside the screen
843     // (else nothing is blitted, causing the animations not to be updated)
844     x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
845     y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
846     x2 = MIN(MAX(1, x2), WIN_XSIZE);
847     y2 = MIN(MAX(1, y2), WIN_YSIZE);
848
849     BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
850   }
851
852   redraw_mask = REDRAW_NONE;
853
854 #if DEBUG_FRAME_TIME
855   PrintFrameTimeDebugging();
856 #endif
857 }
858
859 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
860 {
861   unsigned int frame_delay_value_old = GetVideoFrameDelay();
862
863   SetVideoFrameDelay(frame_delay_value);
864
865   BackToFront();
866
867   SetVideoFrameDelay(frame_delay_value_old);
868 }
869
870 static int fade_type_skip = FADE_TYPE_NONE;
871
872 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
873 {
874   void (*draw_border_function)(void) = NULL;
875   int x, y, width, height;
876   int fade_delay, post_delay;
877
878   if (fade_type == FADE_TYPE_FADE_OUT)
879   {
880     if (fade_type_skip != FADE_TYPE_NONE)
881     {
882       // skip all fade operations until specified fade operation
883       if (fade_type & fade_type_skip)
884         fade_type_skip = FADE_TYPE_NONE;
885
886       return;
887     }
888
889     if (fading.fade_mode & FADE_TYPE_TRANSFORM)
890       return;
891   }
892
893   redraw_mask |= fade_mask;
894
895   if (fade_type == FADE_TYPE_SKIP)
896   {
897     fade_type_skip = fade_mode;
898
899     return;
900   }
901
902   fade_delay = fading.fade_delay;
903   post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
904
905   if (fade_type_skip != FADE_TYPE_NONE)
906   {
907     // skip all fade operations until specified fade operation
908     if (fade_type & fade_type_skip)
909       fade_type_skip = FADE_TYPE_NONE;
910
911     fade_delay = 0;
912   }
913
914   if (global.autoplay_leveldir)
915   {
916     return;
917   }
918
919   if (fade_mask == REDRAW_FIELD)
920   {
921     x = FADE_SX;
922     y = FADE_SY;
923     width  = FADE_SXSIZE;
924     height = FADE_SYSIZE;
925
926     if (border.draw_masked_when_fading)
927       draw_border_function = DrawMaskedBorder_FIELD;    // update when fading
928     else
929       DrawMaskedBorder_FIELD();                         // draw once
930   }
931   else          // REDRAW_ALL
932   {
933     x = 0;
934     y = 0;
935     width  = WIN_XSIZE;
936     height = WIN_YSIZE;
937   }
938
939   // when switching screens without fading, set fade delay to zero
940   if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
941     fade_delay = 0;
942
943   // do not display black frame when fading out without fade delay
944   if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
945     return;
946
947   FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
948                 draw_border_function);
949
950   redraw_mask &= ~fade_mask;
951
952   ClearAutoRepeatKeyEvents();
953 }
954
955 static void SetScreenStates_BeforeFadingIn(void)
956 {
957   // temporarily set screen mode for animations to screen after fading in
958   global.anim_status = global.anim_status_next;
959
960   // store backbuffer with all animations that will be started after fading in
961   PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
962
963   // set screen mode for animations back to fading
964   global.anim_status = GAME_MODE_PSEUDO_FADING;
965 }
966
967 static void SetScreenStates_AfterFadingIn(void)
968 {
969   // store new source screen (to use correct masked border for fading)
970   gfx.fade_border_source_status = global.border_status;
971
972   global.anim_status = global.anim_status_next;
973 }
974
975 static void SetScreenStates_BeforeFadingOut(void)
976 {
977   // store new target screen (to use correct masked border for fading)
978   gfx.fade_border_target_status = game_status;
979
980   // set screen mode for animations to fading
981   global.anim_status = GAME_MODE_PSEUDO_FADING;
982
983   // store backbuffer with all animations that will be stopped for fading out
984   PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
985 }
986
987 static void SetScreenStates_AfterFadingOut(void)
988 {
989   global.border_status = game_status;
990
991   // always use global border for PLAYING when restarting the game
992   if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
993     global.border_status = GAME_MODE_PLAYING;
994 }
995
996 void FadeIn(int fade_mask)
997 {
998   SetScreenStates_BeforeFadingIn();
999
1000 #if 1
1001   DrawMaskedBorder(REDRAW_ALL);
1002 #endif
1003
1004   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1005     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1006   else
1007     FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1008
1009   FADE_SX = REAL_SX;
1010   FADE_SY = REAL_SY;
1011   FADE_SXSIZE = FULL_SXSIZE;
1012   FADE_SYSIZE = FULL_SYSIZE;
1013
1014   // activate virtual buttons depending on upcoming game status
1015   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1016       game_status == GAME_MODE_PLAYING && !tape.playing)
1017     SetOverlayActive(TRUE);
1018
1019   SetScreenStates_AfterFadingIn();
1020
1021   // force update of global animation status in case of rapid screen changes
1022   redraw_mask = REDRAW_ALL;
1023   BackToFront();
1024 }
1025
1026 void FadeOut(int fade_mask)
1027 {
1028   // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1029   if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1030       fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1031     BackToFront();
1032
1033   // when using BD game engine, cover playfield before fading out after a game
1034   if (game_bd.cover_screen)
1035     CoverScreen_BD();
1036
1037   SetScreenStates_BeforeFadingOut();
1038
1039   SetTileCursorActive(FALSE);
1040   SetOverlayActive(FALSE);
1041
1042 #if 0
1043   DrawMaskedBorder(REDRAW_ALL);
1044 #endif
1045
1046   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1047     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1048   else
1049     FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1050
1051   SetScreenStates_AfterFadingOut();
1052 }
1053
1054 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1055 {
1056   static struct TitleFadingInfo fading_leave_stored;
1057
1058   if (set)
1059     fading_leave_stored = fading_leave;
1060   else
1061     fading = fading_leave_stored;
1062 }
1063
1064 void FadeSetEnterMenu(void)
1065 {
1066   fading = menu.enter_menu;
1067
1068   FadeSetLeaveNext(fading, TRUE);       // (keep same fade mode)
1069 }
1070
1071 void FadeSetLeaveMenu(void)
1072 {
1073   fading = menu.leave_menu;
1074
1075   FadeSetLeaveNext(fading, TRUE);       // (keep same fade mode)
1076 }
1077
1078 void FadeSetEnterScreen(void)
1079 {
1080   fading = menu.enter_screen[game_status];
1081
1082   FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);       // store
1083 }
1084
1085 void FadeSetNextScreen(void)
1086 {
1087   fading = menu.next_screen[game_status];
1088
1089   // (do not overwrite fade mode set by FadeSetEnterScreen)
1090   // FadeSetLeaveNext(fading, TRUE);    // (keep same fade mode)
1091 }
1092
1093 void FadeSetLeaveScreen(void)
1094 {
1095   FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);      // recall
1096 }
1097
1098 void FadeSetFromType(int type)
1099 {
1100   if (type & TYPE_ENTER_SCREEN)
1101     FadeSetEnterScreen();
1102   else if (type & TYPE_ENTER)
1103     FadeSetEnterMenu();
1104   else if (type & TYPE_LEAVE)
1105     FadeSetLeaveMenu();
1106 }
1107
1108 void FadeSetDisabled(void)
1109 {
1110   static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1111
1112   fading = fading_none;
1113 }
1114
1115 void FadeSkipNextFadeIn(void)
1116 {
1117   FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1118 }
1119
1120 void FadeSkipNextFadeOut(void)
1121 {
1122   FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1123 }
1124
1125 static int getGlobalGameStatus(int status)
1126 {
1127   return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1128           status == GAME_MODE_SCOREINFO       ? GAME_MODE_SCORES :
1129           status);
1130 }
1131
1132 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1133 {
1134   if (graphic == IMG_UNDEFINED)
1135     return IMG_UNDEFINED;
1136
1137   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1138
1139   return (graphic_info[graphic].bitmap != NULL || redefined ?
1140           graphic : default_graphic);
1141 }
1142
1143 static int getBackgroundImage(int graphic)
1144 {
1145   return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1146 }
1147
1148 static int getGlobalBorderImage(int graphic)
1149 {
1150   return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1151 }
1152
1153 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1154 {
1155   int status = getGlobalGameStatus(status_raw);
1156   int graphic =
1157     (status == GAME_MODE_MAIN    ? IMG_GLOBAL_BORDER_MAIN :
1158      status == GAME_MODE_SCORES  ? IMG_GLOBAL_BORDER_SCORES :
1159      status == GAME_MODE_EDITOR  ? IMG_GLOBAL_BORDER_EDITOR :
1160      status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1161      IMG_GLOBAL_BORDER);
1162   int graphic_final = getGlobalBorderImage(graphic);
1163
1164   return graphic_info[graphic_final].bitmap;
1165 }
1166
1167 void SetBackgroundImage(int graphic, int redraw_mask)
1168 {
1169   struct GraphicInfo *g = &graphic_info[graphic];
1170   struct GraphicInfo g_undefined = { 0 };
1171
1172   if (graphic == IMG_UNDEFINED)
1173     g = &g_undefined;
1174
1175   // always use original size bitmap for backgrounds, if existing
1176   Bitmap *bitmap = (g->bitmaps != NULL &&
1177                     g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1178                     g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1179
1180   // remove every mask before setting mask for window, and
1181   // remove window area mask before setting mask for main or door area
1182   int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1183
1184   // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1185   SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0);   // !!! FIX THIS !!!
1186   SetBackgroundBitmap(bitmap, redraw_mask,
1187                       g->src_x, g->src_y,
1188                       g->width, g->height);
1189 }
1190
1191 void SetWindowBackgroundImageIfDefined(int graphic)
1192 {
1193   if (graphic_info[graphic].bitmap)
1194     SetBackgroundImage(graphic, REDRAW_ALL);
1195 }
1196
1197 void SetMainBackgroundImageIfDefined(int graphic)
1198 {
1199   if (graphic_info[graphic].bitmap)
1200     SetBackgroundImage(graphic, REDRAW_FIELD);
1201 }
1202
1203 void SetDoorBackgroundImageIfDefined(int graphic)
1204 {
1205   if (graphic_info[graphic].bitmap)
1206     SetBackgroundImage(graphic, REDRAW_DOOR_1);
1207 }
1208
1209 void SetWindowBackgroundImage(int graphic)
1210 {
1211   SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1212 }
1213
1214 void SetMainBackgroundImage(int graphic)
1215 {
1216   SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1217 }
1218
1219 void SetDoorBackgroundImage(int graphic)
1220 {
1221   SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1222 }
1223
1224 void SetPanelBackground(void)
1225 {
1226   SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1227 }
1228
1229 void DrawBackground(int x, int y, int width, int height)
1230 {
1231   // "drawto" might still point to playfield buffer here (hall of fame)
1232   ClearRectangleOnBackground(backbuffer, x, y, width, height);
1233
1234   if (IN_GFX_FIELD_FULL(x, y))
1235     redraw_mask |= REDRAW_FIELD;
1236   else if (IN_GFX_DOOR_1(x, y))
1237     redraw_mask |= REDRAW_DOOR_1;
1238   else if (IN_GFX_DOOR_2(x, y))
1239     redraw_mask |= REDRAW_DOOR_2;
1240   else if (IN_GFX_DOOR_3(x, y))
1241     redraw_mask |= REDRAW_DOOR_3;
1242 }
1243
1244 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1245 {
1246   struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1247
1248   if (font->bitmap == NULL)
1249     return;
1250
1251   DrawBackground(x, y, width, height);
1252 }
1253
1254 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1255 {
1256   struct GraphicInfo *g = &graphic_info[graphic];
1257
1258   if (g->bitmap == NULL)
1259     return;
1260
1261   DrawBackground(x, y, width, height);
1262 }
1263
1264 static int game_status_last = -1;
1265 static Bitmap *global_border_bitmap_last = NULL;
1266 static Bitmap *global_border_bitmap = NULL;
1267 static int real_sx_last = -1, real_sy_last = -1;
1268 static int full_sxsize_last = -1, full_sysize_last = -1;
1269 static int dx_last = -1, dy_last = -1;
1270 static int dxsize_last = -1, dysize_last = -1;
1271 static int vx_last = -1, vy_last = -1;
1272 static int vxsize_last = -1, vysize_last = -1;
1273 static int ex_last = -1, ey_last = -1;
1274 static int exsize_last = -1, eysize_last = -1;
1275
1276 boolean CheckIfGlobalBorderHasChanged(void)
1277 {
1278   // if game status has not changed, global border has not changed either
1279   if (game_status == game_status_last)
1280     return FALSE;
1281
1282   // determine and store new global border bitmap for current game status
1283   global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1284
1285   return (global_border_bitmap_last != global_border_bitmap);
1286 }
1287
1288 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED             0
1289
1290 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1291 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1292 {
1293   // if game status has not changed, nothing has to be redrawn
1294   if (game_status == game_status_last)
1295     return FALSE;
1296
1297   // redraw if last screen was title screen
1298   if (game_status_last == GAME_MODE_TITLE)
1299     return TRUE;
1300
1301   // redraw if global screen border has changed
1302   if (CheckIfGlobalBorderHasChanged())
1303     return TRUE;
1304
1305   // redraw if position or size of playfield area has changed
1306   if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1307       full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1308     return TRUE;
1309
1310   // redraw if position or size of door area has changed
1311   if (dx_last != DX || dy_last != DY ||
1312       dxsize_last != DXSIZE || dysize_last != DYSIZE)
1313     return TRUE;
1314
1315   // redraw if position or size of tape area has changed
1316   if (vx_last != VX || vy_last != VY ||
1317       vxsize_last != VXSIZE || vysize_last != VYSIZE)
1318     return TRUE;
1319
1320   // redraw if position or size of editor area has changed
1321   if (ex_last != EX || ey_last != EY ||
1322       exsize_last != EXSIZE || eysize_last != EYSIZE)
1323     return TRUE;
1324
1325   return FALSE;
1326 }
1327 #endif
1328
1329 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1330 {
1331   if (bitmap)
1332     BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1333   else
1334     ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1335 }
1336
1337 void RedrawGlobalBorder(void)
1338 {
1339   Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1340
1341   RedrawGlobalBorderFromBitmap(bitmap);
1342
1343   redraw_mask = REDRAW_ALL;
1344 }
1345
1346 static void RedrawGlobalBorderIfNeeded(void)
1347 {
1348 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1349   if (game_status == game_status_last)
1350     return;
1351 #endif
1352
1353   // copy current draw buffer to later copy back areas that have not changed
1354   if (game_status_last != GAME_MODE_TITLE)
1355     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1356
1357 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1358   if (CheckIfGlobalBorderRedrawIsNeeded())
1359 #else
1360   // determine and store new global border bitmap for current game status
1361   global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1362 #endif
1363   {
1364     // redraw global screen border (or clear, if defined to be empty)
1365     RedrawGlobalBorderFromBitmap(global_border_bitmap);
1366
1367     if (game_status == GAME_MODE_EDITOR)
1368       DrawSpecialEditorDoor();
1369
1370     // copy previous playfield and door areas, if they are defined on both
1371     // previous and current screen and if they still have the same size
1372
1373     if (real_sx_last != -1 && real_sy_last != -1 &&
1374         REAL_SX != -1 && REAL_SY != -1 &&
1375         full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1376       BlitBitmap(bitmap_db_store_1, backbuffer,
1377                  real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1378                  REAL_SX, REAL_SY);
1379
1380     if (dx_last != -1 && dy_last != -1 &&
1381         DX != -1 && DY != -1 &&
1382         dxsize_last == DXSIZE && dysize_last == DYSIZE)
1383       BlitBitmap(bitmap_db_store_1, backbuffer,
1384                  dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1385
1386     if (game_status != GAME_MODE_EDITOR)
1387     {
1388       if (vx_last != -1 && vy_last != -1 &&
1389           VX != -1 && VY != -1 &&
1390           vxsize_last == VXSIZE && vysize_last == VYSIZE)
1391         BlitBitmap(bitmap_db_store_1, backbuffer,
1392                    vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1393     }
1394     else
1395     {
1396       if (ex_last != -1 && ey_last != -1 &&
1397           EX != -1 && EY != -1 &&
1398           exsize_last == EXSIZE && eysize_last == EYSIZE)
1399         BlitBitmap(bitmap_db_store_1, backbuffer,
1400                    ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1401     }
1402
1403     redraw_mask = REDRAW_ALL;
1404   }
1405
1406   game_status_last = game_status;
1407
1408   global_border_bitmap_last = global_border_bitmap;
1409
1410   real_sx_last = REAL_SX;
1411   real_sy_last = REAL_SY;
1412   full_sxsize_last = FULL_SXSIZE;
1413   full_sysize_last = FULL_SYSIZE;
1414   dx_last = DX;
1415   dy_last = DY;
1416   dxsize_last = DXSIZE;
1417   dysize_last = DYSIZE;
1418   vx_last = VX;
1419   vy_last = VY;
1420   vxsize_last = VXSIZE;
1421   vysize_last = VYSIZE;
1422   ex_last = EX;
1423   ey_last = EY;
1424   exsize_last = EXSIZE;
1425   eysize_last = EYSIZE;
1426 }
1427
1428 void ClearField(void)
1429 {
1430   RedrawGlobalBorderIfNeeded();
1431
1432   // !!! "drawto" might still point to playfield buffer here (see above) !!!
1433   // (when entering hall of fame after playing)
1434   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1435
1436   // !!! maybe this should be done before clearing the background !!!
1437   if (game_status == GAME_MODE_PLAYING)
1438   {
1439     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1440     SetDrawtoField(DRAW_TO_FIELDBUFFER);
1441   }
1442   else
1443   {
1444     SetDrawtoField(DRAW_TO_BACKBUFFER);
1445   }
1446 }
1447
1448 void MarkTileDirty(int x, int y)
1449 {
1450   redraw_mask |= REDRAW_FIELD;
1451 }
1452
1453 void SetBorderElement(void)
1454 {
1455   int x, y;
1456
1457   BorderElement = EL_EMPTY;
1458
1459   // only the R'n'D game engine may use an additional steelwall border
1460   if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1461     return;
1462
1463   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1464   {
1465     for (x = 0; x < lev_fieldx; x++)
1466     {
1467       if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1468         BorderElement = EL_STEELWALL;
1469
1470       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1471         x = lev_fieldx - 2;
1472     }
1473   }
1474 }
1475
1476 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1477                        int max_array_fieldx, int max_array_fieldy,
1478                        short field[max_array_fieldx][max_array_fieldy],
1479                        int max_fieldx, int max_fieldy)
1480 {
1481   static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1482   struct XY *check = xy_topdown;
1483   int old_element = field[start_x][start_y];
1484   int stack_pos = 0;
1485
1486   // do nothing if start field already has the desired content
1487   if (old_element == fill_element)
1488     return;
1489
1490   stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1491
1492   while (stack_pos > 0)
1493   {
1494     struct XY current = stack_buffer[--stack_pos];
1495     int i;
1496
1497     field[current.x][current.y] = fill_element;
1498
1499     for (i = 0; i < 4; i++)
1500     {
1501       int x = current.x + check[i].x;
1502       int y = current.y + check[i].y;
1503
1504       // check for stack buffer overflow (should not happen)
1505       if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1506         Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1507
1508       if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1509         stack_buffer[stack_pos++] = (struct XY){ x, y };
1510     }
1511   }
1512 }
1513
1514 void FloodFillLevel(int from_x, int from_y, int fill_element,
1515                     short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1516                     int max_fieldx, int max_fieldy)
1517 {
1518   FloodFillLevelExt(from_x, from_y, fill_element,
1519                     MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1520                     max_fieldx, max_fieldy);
1521 }
1522
1523 void SetRandomAnimationValue(int x, int y)
1524 {
1525   gfx.anim_random_frame = GfxRandom[x][y];
1526 }
1527
1528 void SetAnimationFirstLevel(int first_level)
1529 {
1530   gfx.anim_first_level = first_level;
1531 }
1532
1533 int getGraphicAnimationFrame(int graphic, int sync_frame)
1534 {
1535   // animation synchronized with global frame counter, not move position
1536   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1537     sync_frame = FrameCounter;
1538   else if (graphic_info[graphic].anim_global_anim_sync)
1539     sync_frame = getGlobalAnimSyncFrame();
1540
1541   return getAnimationFrame(graphic_info[graphic].anim_frames,
1542                            graphic_info[graphic].anim_delay,
1543                            graphic_info[graphic].anim_mode,
1544                            graphic_info[graphic].anim_start_frame,
1545                            sync_frame);
1546 }
1547
1548 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1549 {
1550   if (graphic_info[graphic].anim_mode & ANIM_TILED)
1551   {
1552     struct GraphicInfo *g = &graphic_info[graphic];
1553     int xsize = MAX(1, g->anim_frames_per_line);
1554     int ysize = MAX(1, g->anim_frames / xsize);
1555     int xoffset = g->anim_start_frame % xsize;
1556     int yoffset = g->anim_start_frame % ysize;
1557     // may be needed if screen field is significantly larger than playfield
1558     int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1559     int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1560     int sync_frame = y * xsize + x;
1561
1562     return sync_frame % g->anim_frames;
1563   }
1564   else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1565   {
1566     struct GraphicInfo *g = &graphic_info[graphic];
1567     // may be needed if screen field is significantly larger than playfield
1568     int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1569     int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1570     int sync_frame = GfxRandomStatic[x][y];
1571
1572     return sync_frame % g->anim_frames;
1573   }
1574   else
1575   {
1576     int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1577
1578     return getGraphicAnimationFrame(graphic, sync_frame);
1579   }
1580 }
1581
1582 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1583 {
1584   struct GraphicInfo *g = &graphic_info[graphic];
1585   int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1586
1587   if (tilesize == gfx.standard_tile_size)
1588     *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1589   else if (tilesize == game.tile_size)
1590     *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1591   else
1592     *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1593 }
1594
1595 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1596                         boolean get_backside)
1597 {
1598   struct GraphicInfo *g = &graphic_info[graphic];
1599   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1600   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1601
1602   if (g->offset_y == 0)         // frames are ordered horizontally
1603   {
1604     int max_width = g->anim_frames_per_line * g->width;
1605     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1606
1607     *x = pos % max_width;
1608     *y = src_y % g->height + pos / max_width * g->height;
1609   }
1610   else if (g->offset_x == 0)    // frames are ordered vertically
1611   {
1612     int max_height = g->anim_frames_per_line * g->height;
1613     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1614
1615     *x = src_x % g->width + pos / max_height * g->width;
1616     *y = pos % max_height;
1617   }
1618   else                          // frames are ordered diagonally
1619   {
1620     *x = src_x + frame * g->offset_x;
1621     *y = src_y + frame * g->offset_y;
1622   }
1623 }
1624
1625 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1626                               Bitmap **bitmap, int *x, int *y,
1627                               boolean get_backside)
1628 {
1629   struct GraphicInfo *g = &graphic_info[graphic];
1630
1631   // if no graphics defined at all, use fallback graphics
1632   if (g->bitmaps == NULL)
1633     *g = graphic_info[IMG_CHAR_EXCLAM];
1634
1635   // if no in-game graphics defined, always use standard graphic size
1636   if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1637     tilesize = TILESIZE;
1638
1639   getGraphicSourceBitmap(graphic, tilesize, bitmap);
1640   getGraphicSourceXY(graphic, frame, x, y, get_backside);
1641
1642   *x = *x * tilesize / g->tile_size;
1643   *y = *y * tilesize / g->tile_size;
1644 }
1645
1646 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1647                            Bitmap **bitmap, int *x, int *y)
1648 {
1649   getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1650 }
1651
1652 void getFixedGraphicSource(int graphic, int frame,
1653                            Bitmap **bitmap, int *x, int *y)
1654 {
1655   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1656 }
1657
1658 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1659 {
1660   getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1661 }
1662
1663 void getGlobalAnimGraphicSource(int graphic, int frame,
1664                                 Bitmap **bitmap, int *x, int *y)
1665 {
1666   struct GraphicInfo *g = &graphic_info[graphic];
1667
1668   // if no graphics defined at all, use fallback graphics
1669   if (g->bitmaps == NULL)
1670     *g = graphic_info[IMG_CHAR_EXCLAM];
1671
1672   // use original size graphics, if existing, else use standard size graphics
1673   if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1674     *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1675   else
1676     *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1677
1678   getGraphicSourceXY(graphic, frame, x, y, FALSE);
1679 }
1680
1681 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1682                                 int *x, int *y, boolean get_backside)
1683 {
1684   getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1685                            get_backside);
1686 }
1687
1688 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1689 {
1690   getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1691 }
1692
1693 void DrawGraphic(int x, int y, int graphic, int frame)
1694 {
1695 #if DEBUG
1696   if (!IN_SCR_FIELD(x, y))
1697   {
1698     Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1699     Debug("draw:DrawGraphic", "This should never happen!");
1700
1701     return;
1702   }
1703 #endif
1704
1705   DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1706                  frame);
1707
1708   MarkTileDirty(x, y);
1709 }
1710
1711 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1712 {
1713 #if DEBUG
1714   if (!IN_SCR_FIELD(x, y))
1715   {
1716     Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1717           x, y, graphic);
1718     Debug("draw:DrawFixedGraphic", "This should never happen!");
1719
1720     return;
1721   }
1722 #endif
1723
1724   DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1725                       frame);
1726   MarkTileDirty(x, y);
1727 }
1728
1729 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1730                     int frame)
1731 {
1732   Bitmap *src_bitmap;
1733   int src_x, src_y;
1734
1735   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1736
1737   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1738 }
1739
1740 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1741                          int frame)
1742 {
1743   Bitmap *src_bitmap;
1744   int src_x, src_y;
1745
1746   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1747   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1748 }
1749
1750 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1751 {
1752 #if DEBUG
1753   if (!IN_SCR_FIELD(x, y))
1754   {
1755     Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1756           x, y, graphic);
1757     Debug("draw:DrawGraphicThruMask", "This should never happen!");
1758
1759     return;
1760   }
1761 #endif
1762
1763   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1764                          graphic, frame);
1765
1766   MarkTileDirty(x, y);
1767 }
1768
1769 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1770 {
1771 #if DEBUG
1772   if (!IN_SCR_FIELD(x, y))
1773   {
1774     Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1775           x, y, graphic);
1776     Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1777
1778     return;
1779   }
1780 #endif
1781
1782   DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1783                               graphic, frame);
1784   MarkTileDirty(x, y);
1785 }
1786
1787 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1788                             int frame)
1789 {
1790   Bitmap *src_bitmap;
1791   int src_x, src_y;
1792
1793   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1794
1795   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1796                    dst_x, dst_y);
1797 }
1798
1799 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1800                                  int graphic, int frame)
1801 {
1802   Bitmap *src_bitmap;
1803   int src_x, src_y;
1804
1805   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1806
1807   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1808                    dst_x, dst_y);
1809 }
1810
1811 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1812 {
1813   DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1814                       frame, tilesize);
1815   MarkTileDirty(x / tilesize, y / tilesize);
1816 }
1817
1818 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1819                               int tilesize)
1820 {
1821   DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1822                               graphic, frame, tilesize);
1823   MarkTileDirty(x / tilesize, y / tilesize);
1824 }
1825
1826 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1827                          int tilesize)
1828 {
1829   Bitmap *src_bitmap;
1830   int src_x, src_y;
1831
1832   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1833   BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1834 }
1835
1836 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1837                                  int frame, int tilesize)
1838 {
1839   Bitmap *src_bitmap;
1840   int src_x, src_y;
1841
1842   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1843   BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1844 }
1845
1846 void DrawMiniGraphic(int x, int y, int graphic)
1847 {
1848   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1849   MarkTileDirty(x / 2, y / 2);
1850 }
1851
1852 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1853 {
1854   Bitmap *src_bitmap;
1855   int src_x, src_y;
1856
1857   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1858   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1859 }
1860
1861 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1862                                      int graphic, int frame,
1863                                      int cut_mode, int mask_mode)
1864 {
1865   Bitmap *src_bitmap;
1866   int src_x, src_y;
1867   int dst_x, dst_y;
1868   int width = TILEX, height = TILEY;
1869   int cx = 0, cy = 0;
1870
1871   if (dx || dy)                 // shifted graphic
1872   {
1873     if (x < BX1)                // object enters playfield from the left
1874     {
1875       x = BX1;
1876       width = dx;
1877       cx = TILEX - dx;
1878       dx = 0;
1879     }
1880     else if (x > BX2)           // object enters playfield from the right
1881     {
1882       x = BX2;
1883       width = -dx;
1884       dx = TILEX + dx;
1885     }
1886     else if (x == BX1 && dx < 0) // object leaves playfield to the left
1887     {
1888       width += dx;
1889       cx = -dx;
1890       dx = 0;
1891     }
1892     else if (x == BX2 && dx > 0) // object leaves playfield to the right
1893       width -= dx;
1894     else if (dx)                // general horizontal movement
1895       MarkTileDirty(x + SIGN(dx), y);
1896
1897     if (y < BY1)                // object enters playfield from the top
1898     {
1899       if (cut_mode == CUT_BELOW) // object completely above top border
1900         return;
1901
1902       y = BY1;
1903       height = dy;
1904       cy = TILEY - dy;
1905       dy = 0;
1906     }
1907     else if (y > BY2)           // object enters playfield from the bottom
1908     {
1909       y = BY2;
1910       height = -dy;
1911       dy = TILEY + dy;
1912     }
1913     else if (y == BY1 && dy < 0) // object leaves playfield to the top
1914     {
1915       height += dy;
1916       cy = -dy;
1917       dy = 0;
1918     }
1919     else if (dy > 0 && cut_mode == CUT_ABOVE)
1920     {
1921       if (y == BY2)             // object completely above bottom border
1922         return;
1923
1924       height = dy;
1925       cy = TILEY - dy;
1926       dy = TILEY;
1927       MarkTileDirty(x, y + 1);
1928     }                           // object leaves playfield to the bottom
1929     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1930       height -= dy;
1931     else if (dy)                // general vertical movement
1932       MarkTileDirty(x, y + SIGN(dy));
1933   }
1934
1935 #if DEBUG
1936   if (!IN_SCR_FIELD(x, y))
1937   {
1938     Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1939           x, y, graphic);
1940     Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1941
1942     return;
1943   }
1944 #endif
1945
1946   width = width * TILESIZE_VAR / TILESIZE;
1947   height = height * TILESIZE_VAR / TILESIZE;
1948   cx = cx * TILESIZE_VAR / TILESIZE;
1949   cy = cy * TILESIZE_VAR / TILESIZE;
1950   dx = dx * TILESIZE_VAR / TILESIZE;
1951   dy = dy * TILESIZE_VAR / TILESIZE;
1952
1953   if (width > 0 && height > 0)
1954   {
1955     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1956
1957     src_x += cx;
1958     src_y += cy;
1959
1960     dst_x = FX + x * TILEX_VAR + dx;
1961     dst_y = FY + y * TILEY_VAR + dy;
1962
1963     if (mask_mode == USE_MASKING)
1964       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1965                        dst_x, dst_y);
1966     else
1967       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1968                  dst_x, dst_y);
1969
1970     MarkTileDirty(x, y);
1971   }
1972 }
1973
1974 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1975                                      int graphic, int frame,
1976                                      int cut_mode, int mask_mode)
1977 {
1978   Bitmap *src_bitmap;
1979   int src_x, src_y;
1980   int dst_x, dst_y;
1981   int width = TILEX_VAR, height = TILEY_VAR;
1982   int x1 = x;
1983   int y1 = y;
1984   int x2 = x + SIGN(dx);
1985   int y2 = y + SIGN(dy);
1986
1987   // movement with two-tile animations must be sync'ed with movement position,
1988   // not with current GfxFrame (which can be higher when using slow movement)
1989   int anim_pos = (dx ? ABS(dx) : ABS(dy));
1990   int anim_frames = graphic_info[graphic].anim_frames;
1991
1992   // (we also need anim_delay here for movement animations with less frames)
1993   int anim_delay = graphic_info[graphic].anim_delay;
1994   int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1995
1996   boolean draw_start_tile = (cut_mode != CUT_ABOVE);    // only for falling!
1997   boolean draw_end_tile   = (cut_mode != CUT_BELOW);    // only for falling!
1998
1999   // re-calculate animation frame for two-tile movement animation
2000   frame = getGraphicAnimationFrame(graphic, sync_frame);
2001
2002   // check if movement start graphic inside screen area and should be drawn
2003   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
2004   {
2005     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
2006
2007     dst_x = FX + x1 * TILEX_VAR;
2008     dst_y = FY + y1 * TILEY_VAR;
2009
2010     if (mask_mode == USE_MASKING)
2011       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2012                        dst_x, dst_y);
2013     else
2014       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2015                  dst_x, dst_y);
2016
2017     MarkTileDirty(x1, y1);
2018   }
2019
2020   // check if movement end graphic inside screen area and should be drawn
2021   if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2022   {
2023     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2024
2025     dst_x = FX + x2 * TILEX_VAR;
2026     dst_y = FY + y2 * TILEY_VAR;
2027
2028     if (mask_mode == USE_MASKING)
2029       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2030                        dst_x, dst_y);
2031     else
2032       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2033                  dst_x, dst_y);
2034
2035     MarkTileDirty(x2, y2);
2036   }
2037 }
2038
2039 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2040                                int graphic, int frame,
2041                                int cut_mode, int mask_mode)
2042 {
2043   if (graphic < 0)
2044   {
2045     DrawGraphic(x, y, graphic, frame);
2046
2047     return;
2048   }
2049
2050   if (graphic_info[graphic].double_movement)    // EM style movement images
2051     DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2052   else
2053     DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2054 }
2055
2056 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2057                                        int graphic, int frame, int cut_mode)
2058 {
2059   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2060 }
2061
2062 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2063                           int cut_mode, int mask_mode)
2064 {
2065   int lx = LEVELX(x), ly = LEVELY(y);
2066   int graphic;
2067   int frame;
2068
2069   if (IN_LEV_FIELD(lx, ly))
2070   {
2071     if (element == EL_EMPTY)
2072       element = GfxElementEmpty[lx][ly];
2073
2074     SetRandomAnimationValue(lx, ly);
2075
2076     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2077     frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2078
2079     // do not use double (EM style) movement graphic when not moving
2080     if (graphic_info[graphic].double_movement && !dx && !dy)
2081     {
2082       graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2083       frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2084     }
2085
2086     if (game.use_masked_elements && (dx || dy))
2087       mask_mode = USE_MASKING;
2088   }
2089   else  // border element
2090   {
2091     graphic = el2img(element);
2092     frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2093   }
2094
2095   if (element == EL_EXPANDABLE_WALL)
2096   {
2097     boolean left_stopped = FALSE, right_stopped = FALSE;
2098
2099     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2100       left_stopped = TRUE;
2101     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2102       right_stopped = TRUE;
2103
2104     if (left_stopped && right_stopped)
2105       graphic = IMG_WALL;
2106     else if (left_stopped)
2107     {
2108       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2109       frame = graphic_info[graphic].anim_frames - 1;
2110     }
2111     else if (right_stopped)
2112     {
2113       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2114       frame = graphic_info[graphic].anim_frames - 1;
2115     }
2116   }
2117
2118   if (dx || dy)
2119     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2120   else if (mask_mode == USE_MASKING)
2121     DrawGraphicThruMask(x, y, graphic, frame);
2122   else
2123     DrawGraphic(x, y, graphic, frame);
2124 }
2125
2126 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2127                          int cut_mode, int mask_mode)
2128 {
2129   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2130     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2131                          cut_mode, mask_mode);
2132 }
2133
2134 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2135                               int cut_mode)
2136 {
2137   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2138 }
2139
2140 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2141                              int cut_mode)
2142 {
2143   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2144 }
2145
2146 void DrawLevelElementThruMask(int x, int y, int element)
2147 {
2148   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2149 }
2150
2151 void DrawLevelFieldThruMask(int x, int y)
2152 {
2153   DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2154 }
2155
2156 // !!! implementation of quicksand is totally broken !!!
2157 #define IS_CRUMBLED_TILE(x, y, e)                                       \
2158         (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
2159                              !IS_MOVING(x, y) ||                        \
2160                              (e) == EL_QUICKSAND_EMPTYING ||            \
2161                              (e) == EL_QUICKSAND_FAST_EMPTYING))
2162
2163 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2164                                                int graphic)
2165 {
2166   Bitmap *src_bitmap;
2167   int src_x, src_y;
2168   int width, height, cx, cy;
2169   int sx = SCREENX(x), sy = SCREENY(y);
2170   int crumbled_border_size = graphic_info[graphic].border_size;
2171   int crumbled_tile_size = graphic_info[graphic].tile_size;
2172   int crumbled_border_size_var =
2173     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2174   int i;
2175
2176   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2177
2178   for (i = 1; i < 4; i++)
2179   {
2180     int dxx = (i & 1 ? dx : 0);
2181     int dyy = (i & 2 ? dy : 0);
2182     int xx = x + dxx;
2183     int yy = y + dyy;
2184     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2185                    BorderElement);
2186
2187     // check if neighbour field is of same crumble type
2188     boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2189                     graphic_info[graphic].class ==
2190                     graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2191
2192     // return if check prevents inner corner
2193     if (same == (dxx == dx && dyy == dy))
2194       return;
2195   }
2196
2197   // if we reach this point, we have an inner corner
2198
2199   getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2200
2201   width  = crumbled_border_size_var;
2202   height = crumbled_border_size_var;
2203   cx = (dx > 0 ? TILESIZE_VAR - width  : 0);
2204   cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2205
2206   if (game.use_masked_elements)
2207   {
2208     int graphic0 = el2img(EL_EMPTY);
2209     int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2210     Bitmap *src_bitmap0;
2211     int src_x0, src_y0;
2212
2213     getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2214
2215     BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2216                width, height,
2217                FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2218
2219     BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2220                      width, height,
2221                      FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2222   }
2223   else
2224     BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2225                width, height,
2226                FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2227 }
2228
2229 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2230                                           int dir)
2231 {
2232   Bitmap *src_bitmap;
2233   int src_x, src_y;
2234   int width, height, bx, by, cx, cy;
2235   int sx = SCREENX(x), sy = SCREENY(y);
2236   int crumbled_border_size = graphic_info[graphic].border_size;
2237   int crumbled_tile_size = graphic_info[graphic].tile_size;
2238   int crumbled_border_size_var =
2239     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2240   int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2241   int i;
2242
2243   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2244
2245   // only needed when using masked elements
2246   int graphic0 = el2img(EL_EMPTY);
2247   int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2248   Bitmap *src_bitmap0;
2249   int src_x0, src_y0;
2250
2251   if (game.use_masked_elements)
2252     getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2253
2254   // draw simple, sloppy, non-corner-accurate crumbled border
2255
2256   width  = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2257   height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2258   cx = (dir == 2 ? crumbled_border_pos_var : 0);
2259   cy = (dir == 3 ? crumbled_border_pos_var : 0);
2260
2261   if (game.use_masked_elements)
2262   {
2263     BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2264                width, height,
2265                FX + sx * TILEX_VAR + cx,
2266                FY + sy * TILEY_VAR + cy);
2267
2268     BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2269                      width, height,
2270                      FX + sx * TILEX_VAR + cx,
2271                      FY + sy * TILEY_VAR + cy);
2272   }
2273   else
2274     BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2275                width, height,
2276                FX + sx * TILEX_VAR + cx,
2277                FY + sy * TILEY_VAR + cy);
2278
2279   // (remaining middle border part must be at least as big as corner part)
2280   if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2281       crumbled_border_size_var >= TILESIZE_VAR / 3)
2282     return;
2283
2284   // correct corners of crumbled border, if needed
2285
2286   for (i = -1; i <= 1; i += 2)
2287   {
2288     int xx = x + (dir == 0 || dir == 3 ? i : 0);
2289     int yy = y + (dir == 1 || dir == 2 ? i : 0);
2290     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2291                    BorderElement);
2292
2293     // check if neighbour field is of same crumble type
2294     if (IS_CRUMBLED_TILE(xx, yy, element) &&
2295         graphic_info[graphic].class ==
2296         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2297     {
2298       // no crumbled corner, but continued crumbled border
2299
2300       int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2301       int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2302       int b1 = (i == 1 ? crumbled_border_size_var :
2303                 TILESIZE_VAR - 2 * crumbled_border_size_var);
2304
2305       width  = crumbled_border_size_var;
2306       height = crumbled_border_size_var;
2307
2308       if (dir == 1 || dir == 2)
2309       {
2310         cx = c1;
2311         cy = c2;
2312         bx = cx;
2313         by = b1;
2314       }
2315       else
2316       {
2317         cx = c2;
2318         cy = c1;
2319         bx = b1;
2320         by = cy;
2321       }
2322
2323       if (game.use_masked_elements)
2324       {
2325         BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2326                    width, height,
2327                    FX + sx * TILEX_VAR + cx,
2328                    FY + sy * TILEY_VAR + cy);
2329
2330         BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2331                          width, height,
2332                          FX + sx * TILEX_VAR + cx,
2333                          FY + sy * TILEY_VAR + cy);
2334       }
2335       else
2336         BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2337                    width, height,
2338                    FX + sx * TILEX_VAR + cx,
2339                    FY + sy * TILEY_VAR + cy);
2340     }
2341   }
2342 }
2343
2344 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2345 {
2346   int sx = SCREENX(x), sy = SCREENY(y);
2347   int element;
2348   int i;
2349   struct XY *xy = xy_topdown;
2350
2351   if (!IN_LEV_FIELD(x, y))
2352     return;
2353
2354   element = TILE_GFX_ELEMENT(x, y);
2355
2356   if (IS_CRUMBLED_TILE(x, y, element))          // crumble field itself
2357   {
2358     if (!IN_SCR_FIELD(sx, sy))
2359       return;
2360
2361     // crumble field borders towards direct neighbour fields
2362     for (i = 0; i < 4; i++)
2363     {
2364       int xx = x + xy[i].x;
2365       int yy = y + xy[i].y;
2366
2367       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2368                  BorderElement);
2369
2370       // check if neighbour field is of same crumble type
2371       if (IS_CRUMBLED_TILE(xx, yy, element) &&
2372           graphic_info[graphic].class ==
2373           graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2374         continue;
2375
2376       DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2377     }
2378
2379     // crumble inner field corners towards corner neighbour fields
2380     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2381         graphic_info[graphic].anim_frames == 2)
2382     {
2383       for (i = 0; i < 4; i++)
2384       {
2385         int dx = (i & 1 ? +1 : -1);
2386         int dy = (i & 2 ? +1 : -1);
2387
2388         DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2389       }
2390     }
2391
2392     MarkTileDirty(sx, sy);
2393   }
2394   else          // center field is not crumbled -- crumble neighbour fields
2395   {
2396     // crumble field borders of direct neighbour fields
2397     for (i = 0; i < 4; i++)
2398     {
2399       int xx = x + xy[i].x;
2400       int yy = y + xy[i].y;
2401       int sxx = sx + xy[i].x;
2402       int syy = sy + xy[i].y;
2403
2404       if (!IN_LEV_FIELD(xx, yy) ||
2405           !IN_SCR_FIELD(sxx, syy))
2406         continue;
2407
2408       // do not crumble fields that are being digged or snapped
2409       if (Tile[xx][yy] == EL_EMPTY ||
2410           Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2411         continue;
2412
2413       element = TILE_GFX_ELEMENT(xx, yy);
2414
2415       if (!IS_CRUMBLED_TILE(xx, yy, element))
2416         continue;
2417
2418       graphic = el_act2crm(element, ACTION_DEFAULT);
2419
2420       DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2421
2422       MarkTileDirty(sxx, syy);
2423     }
2424
2425     // crumble inner field corners of corner neighbour fields
2426     for (i = 0; i < 4; i++)
2427     {
2428       int dx = (i & 1 ? +1 : -1);
2429       int dy = (i & 2 ? +1 : -1);
2430       int xx = x + dx;
2431       int yy = y + dy;
2432       int sxx = sx + dx;
2433       int syy = sy + dy;
2434
2435       if (!IN_LEV_FIELD(xx, yy) ||
2436           !IN_SCR_FIELD(sxx, syy))
2437         continue;
2438
2439       if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2440         continue;
2441
2442       element = TILE_GFX_ELEMENT(xx, yy);
2443
2444       if (!IS_CRUMBLED_TILE(xx, yy, element))
2445         continue;
2446
2447       graphic = el_act2crm(element, ACTION_DEFAULT);
2448
2449       if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2450           graphic_info[graphic].anim_frames == 2)
2451         DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2452
2453       MarkTileDirty(sxx, syy);
2454     }
2455   }
2456 }
2457
2458 void DrawLevelFieldCrumbled(int x, int y)
2459 {
2460   int graphic;
2461
2462   if (!IN_LEV_FIELD(x, y))
2463     return;
2464
2465   if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2466       GfxElement[x][y] != EL_UNDEFINED &&
2467       GFX_CRUMBLED(GfxElement[x][y]))
2468   {
2469     DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2470
2471     return;
2472   }
2473
2474   graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2475
2476   DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2477 }
2478
2479 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2480                                    int step_frame)
2481 {
2482   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2483   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2484   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2485   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2486   int sx = SCREENX(x), sy = SCREENY(y);
2487
2488   DrawScreenGraphic(sx, sy, graphic1, frame1);
2489   DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2490 }
2491
2492 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2493 {
2494   int sx = SCREENX(x), sy = SCREENY(y);
2495   struct XY *xy = xy_topdown;
2496   int i;
2497
2498   // crumble direct neighbour fields (required for field borders)
2499   for (i = 0; i < 4; i++)
2500   {
2501     int xx = x + xy[i].x;
2502     int yy = y + xy[i].y;
2503     int sxx = sx + xy[i].x;
2504     int syy = sy + xy[i].y;
2505
2506     if (!IN_LEV_FIELD(xx, yy) ||
2507         !IN_SCR_FIELD(sxx, syy) ||
2508         !GFX_CRUMBLED(Tile[xx][yy]) ||
2509         IS_MOVING(xx, yy))
2510       continue;
2511
2512     DrawLevelField(xx, yy);
2513   }
2514
2515   // crumble corner neighbour fields (required for inner field corners)
2516   for (i = 0; i < 4; i++)
2517   {
2518     int dx = (i & 1 ? +1 : -1);
2519     int dy = (i & 2 ? +1 : -1);
2520     int xx = x + dx;
2521     int yy = y + dy;
2522     int sxx = sx + dx;
2523     int syy = sy + dy;
2524
2525     if (!IN_LEV_FIELD(xx, yy) ||
2526         !IN_SCR_FIELD(sxx, syy) ||
2527         !GFX_CRUMBLED(Tile[xx][yy]) ||
2528         IS_MOVING(xx, yy))
2529       continue;
2530
2531     int element = TILE_GFX_ELEMENT(xx, yy);
2532     int graphic = el_act2crm(element, ACTION_DEFAULT);
2533
2534     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2535         graphic_info[graphic].anim_frames == 2)
2536       DrawLevelField(xx, yy);
2537   }
2538 }
2539
2540 static int getBorderElement(int x, int y)
2541 {
2542   int border[7][2] =
2543   {
2544     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
2545     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
2546     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
2547     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2548     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
2549     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
2550     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
2551   };
2552   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2553   int steel_position = (x == -1         && y == -1              ? 0 :
2554                         x == lev_fieldx && y == -1              ? 1 :
2555                         x == -1         && y == lev_fieldy      ? 2 :
2556                         x == lev_fieldx && y == lev_fieldy      ? 3 :
2557                         x == -1         || x == lev_fieldx      ? 4 :
2558                         y == -1         || y == lev_fieldy      ? 5 : 6);
2559
2560   return border[steel_position][steel_type];
2561 }
2562
2563 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2564 {
2565   if (game.use_masked_elements)
2566   {
2567     if (graphic != el2img(EL_EMPTY))
2568       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2569
2570     DrawGraphicThruMask(x, y, graphic, frame);
2571   }
2572   else
2573   {
2574     DrawGraphic(x, y, graphic, frame);
2575   }
2576 }
2577
2578 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2579 {
2580   DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2581 }
2582
2583 void DrawScreenElement(int x, int y, int element)
2584 {
2585   int mask_mode = NO_MASKING;
2586
2587   if (game.use_masked_elements)
2588   {
2589     int lx = LEVELX(x), ly = LEVELY(y);
2590
2591     if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2592     {
2593       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2594
2595       mask_mode = USE_MASKING;
2596     }
2597   }
2598
2599   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2600   DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2601 }
2602
2603 void DrawLevelElement(int x, int y, int element)
2604 {
2605   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2606     DrawScreenElement(SCREENX(x), SCREENY(y), element);
2607 }
2608
2609 void DrawScreenField(int x, int y)
2610 {
2611   int lx = LEVELX(x), ly = LEVELY(y);
2612   int element, content;
2613
2614   if (!IN_LEV_FIELD(lx, ly))
2615   {
2616     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2617       element = EL_EMPTY;
2618     else
2619       element = getBorderElement(lx, ly);
2620
2621     DrawScreenElement(x, y, element);
2622
2623     return;
2624   }
2625
2626   element = Tile[lx][ly];
2627   content = Store[lx][ly];
2628
2629   if (IS_MOVING(lx, ly))
2630   {
2631     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2632     boolean cut_mode = NO_CUTTING;
2633
2634     if (element == EL_QUICKSAND_EMPTYING ||
2635         element == EL_QUICKSAND_FAST_EMPTYING ||
2636         element == EL_MAGIC_WALL_EMPTYING ||
2637         element == EL_BD_MAGIC_WALL_EMPTYING ||
2638         element == EL_DC_MAGIC_WALL_EMPTYING ||
2639         element == EL_AMOEBA_DROPPING)
2640       cut_mode = CUT_ABOVE;
2641     else if (element == EL_QUICKSAND_FILLING ||
2642              element == EL_QUICKSAND_FAST_FILLING ||
2643              element == EL_MAGIC_WALL_FILLING ||
2644              element == EL_BD_MAGIC_WALL_FILLING ||
2645              element == EL_DC_MAGIC_WALL_FILLING)
2646       cut_mode = CUT_BELOW;
2647
2648     if (cut_mode == CUT_ABOVE)
2649       DrawScreenElement(x, y, element);
2650     else
2651       DrawScreenElement(x, y, EL_EMPTY);
2652
2653     if (cut_mode != CUT_BELOW && game.use_masked_elements)
2654     {
2655       int dir = MovDir[lx][ly];
2656       int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2657       int newy = y + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2658
2659       if (IN_SCR_FIELD(newx, newy))
2660         DrawScreenElement(newx, newy, EL_EMPTY);
2661     }
2662
2663     if (horiz_move)
2664       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2665     else if (cut_mode == NO_CUTTING)
2666       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2667     else
2668     {
2669       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2670
2671       if (cut_mode == CUT_BELOW &&
2672           IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2673         DrawLevelElement(lx, ly + 1, element);
2674     }
2675
2676     if (content == EL_ACID)
2677     {
2678       int dir = MovDir[lx][ly];
2679       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2680       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2681
2682       DrawLevelElementThruMask(newlx, newly, EL_ACID);
2683
2684       // prevent target field from being drawn again (but without masking)
2685       // (this would happen if target field is scanned after moving element)
2686       Stop[newlx][newly] = TRUE;
2687     }
2688   }
2689   else if (IS_BLOCKED(lx, ly))
2690   {
2691     int oldx, oldy;
2692     int sx, sy;
2693     int horiz_move;
2694     boolean cut_mode = NO_CUTTING;
2695     int element_old, content_old;
2696
2697     Blocked2Moving(lx, ly, &oldx, &oldy);
2698     sx = SCREENX(oldx);
2699     sy = SCREENY(oldy);
2700     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2701                   MovDir[oldx][oldy] == MV_RIGHT);
2702
2703     element_old = Tile[oldx][oldy];
2704     content_old = Store[oldx][oldy];
2705
2706     if (element_old == EL_QUICKSAND_EMPTYING ||
2707         element_old == EL_QUICKSAND_FAST_EMPTYING ||
2708         element_old == EL_MAGIC_WALL_EMPTYING ||
2709         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2710         element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2711         element_old == EL_AMOEBA_DROPPING)
2712       cut_mode = CUT_ABOVE;
2713
2714     DrawScreenElement(x, y, EL_EMPTY);
2715
2716     if (horiz_move)
2717       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2718                                NO_CUTTING);
2719     else if (cut_mode == NO_CUTTING)
2720       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2721                                cut_mode);
2722     else
2723       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2724                                cut_mode);
2725   }
2726   else if (IS_DRAWABLE(element))
2727     DrawScreenElement(x, y, element);
2728   else
2729     DrawScreenElement(x, y, EL_EMPTY);
2730 }
2731
2732 void DrawLevelField(int x, int y)
2733 {
2734   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2735     DrawScreenField(SCREENX(x), SCREENY(y));
2736   else if (IS_MOVING(x, y))
2737   {
2738     int newx, newy;
2739
2740     Moving2Blocked(x, y, &newx, &newy);
2741     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2742       DrawScreenField(SCREENX(newx), SCREENY(newy));
2743   }
2744   else if (IS_BLOCKED(x, y))
2745   {
2746     int oldx, oldy;
2747
2748     Blocked2Moving(x, y, &oldx, &oldy);
2749     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2750       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2751   }
2752 }
2753
2754 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2755                                 int (*el2img_function)(int), boolean masked,
2756                                 int element_bits_draw)
2757 {
2758   int element_base = map_mm_wall_element(element);
2759   int element_bits = (IS_DF_WALL(element) ?
2760                       element - EL_DF_WALL_START :
2761                       IS_MM_WALL(element) ?
2762                       element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2763   int graphic = el2img_function(element_base);
2764   int tilesize_draw = tilesize / 2;
2765   Bitmap *src_bitmap;
2766   int src_x, src_y;
2767   int i;
2768
2769   getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2770
2771   for (i = 0; i < 4; i++)
2772   {
2773     int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2774     int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2775
2776     if (!(element_bits_draw & (1 << i)))
2777       continue;
2778
2779     if (element_bits & (1 << i))
2780     {
2781       if (masked)
2782         BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2783                          tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2784       else
2785         BlitBitmap(src_bitmap, drawto, src_x, src_y,
2786                    tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2787     }
2788     else
2789     {
2790       if (!masked)
2791         ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2792                        tilesize_draw, tilesize_draw);
2793     }
2794   }
2795 }
2796
2797 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2798                            boolean masked, int element_bits_draw)
2799 {
2800   DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2801                       element, tilesize, el2edimg, masked, element_bits_draw);
2802 }
2803
2804 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2805                              int (*el2img_function)(int))
2806 {
2807   DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2808                       0x000f);
2809 }
2810
2811 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2812                                 boolean masked)
2813 {
2814   if (IS_MM_WALL(element))
2815   {
2816     DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2817                         element, tilesize, el2edimg, masked, 0x000f);
2818   }
2819   else
2820   {
2821     int graphic, frame;
2822
2823     el2edimg_with_frame(element, &graphic, &frame);
2824
2825     if (masked)
2826       DrawSizedGraphicThruMask(x, y, graphic, frame, tilesize);
2827     else
2828       DrawSizedGraphic(x, y, graphic, frame, tilesize);
2829   }
2830 }
2831
2832 void DrawSizedElement(int x, int y, int element, int tilesize)
2833 {
2834   DrawSizedElementExt(x, y, element, tilesize, FALSE);
2835 }
2836
2837 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2838 {
2839   DrawSizedElementExt(x, y, element, tilesize, TRUE);
2840 }
2841
2842 void DrawMiniElement(int x, int y, int element)
2843 {
2844   int graphic;
2845
2846   graphic = el2edimg(element);
2847   DrawMiniGraphic(x, y, graphic);
2848 }
2849
2850 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2851                             int tilesize)
2852 {
2853   int x = sx + scroll_x, y = sy + scroll_y;
2854
2855   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2856     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2857   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2858     DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2859   else
2860     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2861 }
2862
2863 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2864 {
2865   int x = sx + scroll_x, y = sy + scroll_y;
2866
2867   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2868     DrawMiniElement(sx, sy, EL_EMPTY);
2869   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2870     DrawMiniElement(sx, sy, Tile[x][y]);
2871   else
2872     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2873 }
2874
2875 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2876                                         int x, int y, int xsize, int ysize,
2877                                         int tile_width, int tile_height)
2878 {
2879   Bitmap *src_bitmap;
2880   int src_x, src_y;
2881   int dst_x = startx + x * tile_width;
2882   int dst_y = starty + y * tile_height;
2883   int width  = graphic_info[graphic].width;
2884   int height = graphic_info[graphic].height;
2885   int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
2886   int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2887   int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
2888   int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2889   int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
2890   int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2891   boolean draw_masked = graphic_info[graphic].draw_masked;
2892
2893   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2894
2895   if (src_bitmap == NULL || width < tile_width || height < tile_height)
2896   {
2897     ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2898     return;
2899   }
2900
2901   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
2902             inner_sx + (x - 1) * tile_width  % inner_width);
2903   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2904             inner_sy + (y - 1) * tile_height % inner_height);
2905
2906   if (draw_masked)
2907     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2908                      dst_x, dst_y);
2909   else
2910     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2911                dst_x, dst_y);
2912 }
2913
2914 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2915                                    int x, int y, int xsize, int ysize,
2916                                    int font_nr)
2917 {
2918   int font_width  = getFontWidth(font_nr);
2919   int font_height = getFontHeight(font_nr);
2920
2921   DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2922                               font_width, font_height);
2923 }
2924
2925 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2926 {
2927   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2928   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2929   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2930   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2931   boolean no_delay = (tape.warp_forward);
2932   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2933   int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2934   DelayCounter anim_delay = { anim_delay_value };
2935   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2936   int font_width = getFontWidth(font_nr);
2937   int font_height = getFontHeight(font_nr);
2938   int max_xsize = level.envelope[envelope_nr].xsize;
2939   int max_ysize = level.envelope[envelope_nr].ysize;
2940   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2941   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2942   int xend = max_xsize;
2943   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2944   int xstep = (xstart < xend ? 1 : 0);
2945   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2946   int start = 0;
2947   int end = MAX(xend - xstart, yend - ystart);
2948   int i;
2949
2950   for (i = start; i <= end; i++)
2951   {
2952     int last_frame = end;       // last frame of this "for" loop
2953     int x = xstart + i * xstep;
2954     int y = ystart + i * ystep;
2955     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2956     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2957     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2958     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2959     int xx, yy;
2960
2961     SetDrawtoField(DRAW_TO_FIELDBUFFER);
2962
2963     BlitScreenToBitmap(backbuffer);
2964
2965     SetDrawtoField(DRAW_TO_BACKBUFFER);
2966
2967     for (yy = 0; yy < ysize; yy++)
2968       for (xx = 0; xx < xsize; xx++)
2969         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2970
2971     DrawTextArea(sx + font_width, sy + font_height,
2972                  level.envelope[envelope_nr].text, font_nr, max_xsize,
2973                  xsize - 2, ysize - 2, 0, mask_mode,
2974                  level.envelope[envelope_nr].autowrap,
2975                  level.envelope[envelope_nr].centered, FALSE);
2976
2977     redraw_mask |= REDRAW_FIELD;
2978     BackToFront();
2979
2980     SkipUntilDelayReached(&anim_delay, &i, last_frame);
2981   }
2982
2983   ClearAutoRepeatKeyEvents();
2984 }
2985
2986 void ShowEnvelope(int envelope_nr)
2987 {
2988   int element = EL_ENVELOPE_1 + envelope_nr;
2989   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2990   int sound_opening = element_info[element].sound[ACTION_OPENING];
2991   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2992   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2993   boolean no_delay = (tape.warp_forward);
2994   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2995   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2996   int anim_mode = graphic_info[graphic].anim_mode;
2997   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2998                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2999   boolean overlay_enabled = GetOverlayEnabled();
3000
3001   game.envelope_active = TRUE;  // needed for RedrawPlayfield() events
3002
3003   SetOverlayEnabled(FALSE);
3004   UnmapAllGadgets();
3005
3006   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3007
3008   if (anim_mode == ANIM_DEFAULT)
3009     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
3010
3011   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
3012
3013   if (tape.playing)
3014     Delay_WithScreenUpdates(wait_delay_value);
3015   else
3016     WaitForEventToContinue();
3017
3018   RemapAllGadgets();
3019   SetOverlayEnabled(overlay_enabled);
3020
3021   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3022
3023   if (anim_mode != ANIM_NONE)
3024     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3025
3026   if (anim_mode == ANIM_DEFAULT)
3027     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3028
3029   game.envelope_active = FALSE;
3030
3031   SetDrawtoField(DRAW_TO_FIELDBUFFER);
3032
3033   redraw_mask |= REDRAW_FIELD;
3034   BackToFront();
3035 }
3036
3037 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3038                                            int xsize, int ysize)
3039 {
3040   if (!global.use_envelope_request)
3041     return;
3042
3043   if (request.bitmap == NULL ||
3044       xsize > request.xsize ||
3045       ysize > request.ysize)
3046   {
3047     if (request.bitmap != NULL)
3048       FreeBitmap(request.bitmap);
3049
3050     request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3051
3052     SDL_Surface *surface = request.bitmap->surface;
3053
3054     if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3055       Fail("SDLGetNativeSurface() failed");
3056   }
3057
3058   BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3059
3060   // create masked surface for request bitmap, if needed
3061   if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3062   {
3063     SDL_Surface *surface        = request.bitmap->surface;
3064     SDL_Surface *surface_masked = request.bitmap->surface_masked;
3065
3066     SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3067     SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3068                     SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3069   }
3070
3071   SDLFreeBitmapTextures(request.bitmap);
3072   SDLCreateBitmapTextures(request.bitmap);
3073
3074   ResetBitmapAlpha(request.bitmap);
3075
3076   // set envelope request run-time values
3077   request.sx = sx;
3078   request.sy = sy;
3079   request.xsize = xsize;
3080   request.ysize = ysize;
3081 }
3082
3083 void DrawEnvelopeRequestToScreen(int drawing_target)
3084 {
3085   if (global.use_envelope_request &&
3086       game.request_active &&
3087       drawing_target == DRAW_TO_SCREEN)
3088   {
3089     struct GraphicInfo *g = &graphic_info[IMG_BACKGROUND_REQUEST];
3090
3091     SetBitmapAlphaNextBlit(request.bitmap, g->alpha);
3092
3093     if (g->draw_masked)
3094       BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3095                          request.sx, request.sy);
3096     else
3097       BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3098                    request.sx, request.sy);
3099   }
3100 }
3101
3102 static void setRequestBasePosition(int *x, int *y)
3103 {
3104   int sx_base, sy_base;
3105
3106   if (request.x != -1)
3107     sx_base = request.x;
3108   else if (request.align == ALIGN_LEFT)
3109     sx_base = SX;
3110   else if (request.align == ALIGN_RIGHT)
3111     sx_base = SX + SXSIZE;
3112   else
3113     sx_base = SX + SXSIZE / 2;
3114
3115   if (request.y != -1)
3116     sy_base = request.y;
3117   else if (request.valign == VALIGN_TOP)
3118     sy_base = SY;
3119   else if (request.valign == VALIGN_BOTTOM)
3120     sy_base = SY + SYSIZE;
3121   else
3122     sy_base = SY + SYSIZE / 2;
3123
3124   *x = sx_base;
3125   *y = sy_base;
3126 }
3127
3128 static void setRequestPositionExt(int *x, int *y, int width, int height,
3129                                   boolean add_border_size)
3130 {
3131   int border_size = request.border_size;
3132   int sx_base, sy_base;
3133   int sx, sy;
3134
3135   setRequestBasePosition(&sx_base, &sy_base);
3136
3137   if (request.align == ALIGN_LEFT)
3138     sx = sx_base;
3139   else if (request.align == ALIGN_RIGHT)
3140     sx = sx_base - width;
3141   else
3142     sx = sx_base - width  / 2;
3143
3144   if (request.valign == VALIGN_TOP)
3145     sy = sy_base;
3146   else if (request.valign == VALIGN_BOTTOM)
3147     sy = sy_base - height;
3148   else
3149     sy = sy_base - height / 2;
3150
3151   sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3152   sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3153
3154   if (add_border_size)
3155   {
3156     sx += border_size;
3157     sy += border_size;
3158   }
3159
3160   *x = sx;
3161   *y = sy;
3162 }
3163
3164 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3165 {
3166   setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3167 }
3168
3169 static void DrawEnvelopeRequestText(int sx, int sy, char *text)
3170 {
3171   char *text_final = text;
3172   char *text_door_style = NULL;
3173   int graphic = IMG_BACKGROUND_REQUEST;
3174   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3175   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3176   int font_nr = FONT_REQUEST;
3177   int font_width = getFontWidth(font_nr);
3178   int font_height = getFontHeight(font_nr);
3179   int border_size = request.border_size;
3180   int line_spacing = request.line_spacing;
3181   int line_height = font_height + line_spacing;
3182   int max_text_width  = request.width  - 2 * border_size;
3183   int max_text_height = request.height - 2 * border_size;
3184   int line_length = max_text_width  / font_width;
3185   int max_lines   = max_text_height / line_height;
3186   int text_width = line_length * font_width;
3187   int sx_offset = border_size;
3188   int sy_offset = border_size;
3189
3190   // force DOOR font inside door area
3191   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3192
3193   if (request.centered)
3194     sx_offset = (request.width - text_width) / 2;
3195
3196   if (request.wrap_single_words && !request.autowrap)
3197   {
3198     char *src_text_ptr, *dst_text_ptr;
3199
3200     if (maxWordLengthInRequestString(text) > line_length)
3201     {
3202       font_nr = FONT_REQUEST_NARROW;
3203       font_width = getFontWidth(font_nr);
3204       line_length = max_text_width  / font_width;
3205     }
3206
3207     text_door_style = checked_malloc(2 * strlen(text) + 1);
3208
3209     src_text_ptr = text;
3210     dst_text_ptr = text_door_style;
3211
3212     while (*src_text_ptr)
3213     {
3214       if (*src_text_ptr == ' ' ||
3215           *src_text_ptr == '?' ||
3216           *src_text_ptr == '!')
3217         *dst_text_ptr++ = '\n';
3218
3219       if (*src_text_ptr != ' ')
3220         *dst_text_ptr++ = *src_text_ptr;
3221
3222       src_text_ptr++;
3223     }
3224
3225     *dst_text_ptr = '\0';
3226
3227     text_final = text_door_style;
3228   }
3229
3230   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3231                  line_length, -1, max_lines, line_spacing, mask_mode,
3232                  request.autowrap, request.centered, FALSE);
3233
3234   if (text_door_style)
3235     free(text_door_style);
3236
3237   ResetFontStatus();
3238 }
3239
3240 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3241 {
3242   DrawBuffer *drawto_last = drawto;
3243   int graphic = IMG_BACKGROUND_REQUEST;
3244   int width = request.width;
3245   int height = request.height;
3246   int tile_size = MAX(request.step_offset, 1);
3247   int x_steps = width  / tile_size;
3248   int y_steps = height / tile_size;
3249   int sx, sy;
3250   int x, y;
3251
3252   setRequestPosition(&sx, &sy, FALSE);
3253
3254   // draw complete envelope request to temporary bitmap
3255   drawto = bitmap_db_store_1;
3256
3257   ClearRectangle(drawto, sx, sy, width, height);
3258
3259   for (y = 0; y < y_steps; y++)
3260     for (x = 0; x < x_steps; x++)
3261       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3262                                   x, y, x_steps, y_steps,
3263                                   tile_size, tile_size);
3264
3265   // write text for request
3266   DrawEnvelopeRequestText(sx, sy, text);
3267
3268   MapToolButtons(req_state);
3269
3270   // restore pointer to drawing buffer
3271   drawto = drawto_last;
3272
3273   // prepare complete envelope request from temporary bitmap
3274   PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3275 }
3276
3277 static void AnimateEnvelopeRequest(int anim_mode, int action)
3278 {
3279   boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3280   int delay_value_normal = request.step_delay;
3281   int delay_value_fast = delay_value_normal / 2;
3282   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3283   boolean no_delay = (tape.warp_forward);
3284   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3285   int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3286   DelayCounter anim_delay = { anim_delay_value };
3287
3288   int tile_size = MAX(request.step_offset, 1);
3289   int max_xsize = request.width  / tile_size;
3290   int max_ysize = request.height / tile_size;
3291   int max_xsize_inner = max_xsize - 2;
3292   int max_ysize_inner = max_ysize - 2;
3293
3294   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3295   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3296   int xend = max_xsize_inner;
3297   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3298   int xstep = (xstart < xend ? 1 : 0);
3299   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3300   int start = 0;
3301   int end = MAX(xend - xstart, yend - ystart);
3302   int i;
3303
3304   if (setup.quick_doors)
3305   {
3306     xstart = xend;
3307     ystart = yend;
3308     end = 0;
3309   }
3310
3311   for (i = start; i <= end; i++)
3312   {
3313     int last_frame = end;       // last frame of this "for" loop
3314     int x = xstart + i * xstep;
3315     int y = ystart + i * ystep;
3316     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3317     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3318     int xsize_size_left = (xsize - 1) * tile_size;
3319     int ysize_size_top  = (ysize - 1) * tile_size;
3320     int max_xsize_pos = (max_xsize - 1) * tile_size;
3321     int max_ysize_pos = (max_ysize - 1) * tile_size;
3322     int width  = xsize * tile_size;
3323     int height = ysize * tile_size;
3324     int src_x, src_y;
3325     int dst_x, dst_y;
3326     int xx, yy;
3327
3328     if (game_ended)
3329       HandleGameActions();
3330
3331     setRequestPosition(&src_x, &src_y, FALSE);
3332     setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3333
3334     for (yy = 0; yy < 2; yy++)
3335     {
3336       for (xx = 0; xx < 2; xx++)
3337       {
3338         int src_xx = src_x + xx * max_xsize_pos;
3339         int src_yy = src_y + yy * max_ysize_pos;
3340         int dst_xx = dst_x + xx * xsize_size_left;
3341         int dst_yy = dst_y + yy * ysize_size_top;
3342         int xx_size = (xx ? tile_size : xsize_size_left);
3343         int yy_size = (yy ? tile_size : ysize_size_top);
3344
3345         // draw partial (animated) envelope request to temporary bitmap
3346         BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3347                    src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3348       }
3349     }
3350
3351     // prepare partial (animated) envelope request from temporary bitmap
3352     PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3353                                    width, height);
3354
3355     redraw_mask |= REDRAW_FIELD;
3356
3357     BackToFront();
3358
3359     SkipUntilDelayReached(&anim_delay, &i, last_frame);
3360   }
3361
3362   ClearAutoRepeatKeyEvents();
3363 }
3364
3365 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3366 {
3367   int graphic = IMG_BACKGROUND_REQUEST;
3368   int sound_opening = SND_REQUEST_OPENING;
3369   int sound_closing = SND_REQUEST_CLOSING;
3370   int anim_mode_1 = request.anim_mode;                  // (higher priority)
3371   int anim_mode_2 = graphic_info[graphic].anim_mode;    // (lower priority)
3372   int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3373   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3374                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3375
3376   game.envelope_active = TRUE;  // needed for RedrawPlayfield() events
3377
3378   if (action == ACTION_OPENING)
3379   {
3380     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3381
3382     if (anim_mode == ANIM_DEFAULT)
3383       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3384
3385     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3386   }
3387   else
3388   {
3389     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3390
3391     if (anim_mode != ANIM_NONE)
3392       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3393
3394     if (anim_mode == ANIM_DEFAULT)
3395       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3396   }
3397
3398   game.envelope_active = FALSE;
3399 }
3400
3401 static Bitmap *GetPreviewTileBitmap(Bitmap *bitmap)
3402 {
3403   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
3404     return GetPreviewTileBitmap_BD(bitmap);
3405
3406   return bitmap;
3407 }
3408
3409 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3410 {
3411   if (IS_MM_WALL(element))
3412   {
3413     DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3414   }
3415   else
3416   {
3417     Bitmap *src_bitmap;
3418     int src_x, src_y;
3419     int graphic = el2preimg(element);
3420
3421     getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3422
3423     // for BD style levels, maybe use bitmap with level-specific colors
3424     src_bitmap = GetPreviewTileBitmap(src_bitmap);
3425
3426     BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3427                dst_x, dst_y);
3428   }
3429 }
3430
3431 void DrawLevel(int draw_background_mask)
3432 {
3433   int x, y;
3434
3435   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3436   SetDrawBackgroundMask(draw_background_mask);
3437
3438   ClearField();
3439
3440   for (x = BX1; x <= BX2; x++)
3441     for (y = BY1; y <= BY2; y++)
3442       DrawScreenField(x, y);
3443
3444   redraw_mask |= REDRAW_FIELD;
3445 }
3446
3447 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3448                     int tilesize)
3449 {
3450   int x, y;
3451
3452   for (x = 0; x < size_x; x++)
3453     for (y = 0; y < size_y; y++)
3454       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3455
3456   redraw_mask |= REDRAW_FIELD;
3457 }
3458
3459 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3460 {
3461   int x, y;
3462
3463   for (x = 0; x < size_x; x++)
3464     for (y = 0; y < size_y; y++)
3465       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3466
3467   redraw_mask |= REDRAW_FIELD;
3468 }
3469
3470 static int getPreviewLevelWidth(void)
3471 {
3472   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
3473     return (level.native_bd_level->cave->x2 - level.native_bd_level->cave->x1 + 1);
3474
3475   return lev_fieldx;
3476 }
3477
3478 static int getPreviewLevelHeight(void)
3479 {
3480   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
3481     return (level.native_bd_level->cave->y2 - level.native_bd_level->cave->y1 + 1);
3482
3483   return lev_fieldy;
3484 }
3485
3486 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3487 {
3488   boolean show_level_border = (BorderElement != EL_EMPTY);
3489   int level_xsize = getPreviewLevelWidth()  + (show_level_border ? 2 : 0);
3490   int level_ysize = getPreviewLevelHeight() + (show_level_border ? 2 : 0);
3491   int tile_size = preview.tile_size;
3492   int preview_width  = preview.xsize * tile_size;
3493   int preview_height = preview.ysize * tile_size;
3494   int real_preview_xsize = MIN(level_xsize, preview.xsize);
3495   int real_preview_ysize = MIN(level_ysize, preview.ysize);
3496   int real_preview_width  = real_preview_xsize * tile_size;
3497   int real_preview_height = real_preview_ysize * tile_size;
3498   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3499   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3500   int x, y;
3501
3502   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3503     return;
3504
3505   DrawBackground(dst_x, dst_y, preview_width, preview_height);
3506
3507   dst_x += (preview_width  - real_preview_width)  / 2;
3508   dst_y += (preview_height - real_preview_height) / 2;
3509
3510   for (x = 0; x < real_preview_xsize; x++)
3511   {
3512     for (y = 0; y < real_preview_ysize; y++)
3513     {
3514       int lx = from_x + x + (show_level_border ? -1 : 0);
3515       int ly = from_y + y + (show_level_border ? -1 : 0);
3516       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3517                      getBorderElement(lx, ly));
3518
3519       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3520                          element, tile_size);
3521     }
3522   }
3523
3524   redraw_mask |= REDRAW_FIELD;
3525 }
3526
3527 #define MICROLABEL_EMPTY                0
3528 #define MICROLABEL_LEVEL_NAME           1
3529 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
3530 #define MICROLABEL_LEVEL_AUTHOR         3
3531 #define MICROLABEL_IMPORTED_FROM_HEAD   4
3532 #define MICROLABEL_IMPORTED_FROM        5
3533 #define MICROLABEL_IMPORTED_BY_HEAD     6
3534 #define MICROLABEL_IMPORTED_BY          7
3535
3536 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3537 {
3538   int max_text_width = SXSIZE;
3539   int font_width = getFontWidth(font_nr);
3540
3541   if (pos->align == ALIGN_CENTER)
3542     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3543   else if (pos->align == ALIGN_RIGHT)
3544     max_text_width = pos->x;
3545   else
3546     max_text_width = SXSIZE - pos->x;
3547
3548   return max_text_width / font_width;
3549 }
3550
3551 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3552 {
3553   char label_text[MAX_OUTPUT_LINESIZE + 1];
3554   int max_len_label_text;
3555   int font_nr = pos->font;
3556   int i;
3557
3558   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3559     return;
3560
3561   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3562       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3563       mode == MICROLABEL_IMPORTED_BY_HEAD)
3564     font_nr = pos->font_alt;
3565
3566   max_len_label_text = getMaxTextLength(pos, font_nr);
3567
3568   if (pos->size != -1)
3569     max_len_label_text = pos->size;
3570
3571   for (i = 0; i < max_len_label_text; i++)
3572     label_text[i] = ' ';
3573   label_text[max_len_label_text] = '\0';
3574
3575   if (strlen(label_text) > 0)
3576     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3577
3578   strncpy(label_text,
3579           (mode == MICROLABEL_LEVEL_NAME ? level.name :
3580            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3581            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3582            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3583            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3584            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3585            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3586           max_len_label_text);
3587   label_text[max_len_label_text] = '\0';
3588
3589   if (strlen(label_text) > 0)
3590     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3591
3592   redraw_mask |= REDRAW_FIELD;
3593 }
3594
3595 static void DrawPreviewLevelLabel(int mode)
3596 {
3597   DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3598 }
3599
3600 static void DrawPreviewLevelInfo(int mode)
3601 {
3602   if (mode == MICROLABEL_LEVEL_NAME)
3603     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3604   else if (mode == MICROLABEL_LEVEL_AUTHOR)
3605     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3606 }
3607
3608 static void DrawPreviewLevelExt(boolean restart)
3609 {
3610   static DelayCounter scroll_delay = { 0 };
3611   static DelayCounter label_delay = { 0 };
3612   static int from_x, from_y, scroll_direction;
3613   static int label_state, label_counter;
3614   boolean show_level_border = (BorderElement != EL_EMPTY);
3615   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3616   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3617
3618   scroll_delay.value = preview.step_delay;
3619   label_delay.value = MICROLEVEL_LABEL_DELAY;
3620
3621   if (restart)
3622   {
3623     from_x = 0;
3624     from_y = 0;
3625
3626     if (preview.anim_mode == ANIM_CENTERED)
3627     {
3628       if (level_xsize > preview.xsize)
3629         from_x = (level_xsize - preview.xsize) / 2;
3630       if (level_ysize > preview.ysize)
3631         from_y = (level_ysize - preview.ysize) / 2;
3632     }
3633
3634     from_x += preview.xoffset;
3635     from_y += preview.yoffset;
3636
3637     scroll_direction = MV_RIGHT;
3638     label_state = 1;
3639     label_counter = 0;
3640
3641     DrawPreviewLevelPlayfield(from_x, from_y);
3642     DrawPreviewLevelLabel(label_state);
3643
3644     DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3645     DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3646
3647     // initialize delay counters
3648     ResetDelayCounter(&scroll_delay);
3649     ResetDelayCounter(&label_delay);
3650
3651     if (leveldir_current->name)
3652     {
3653       struct TextPosInfo *pos = &menu.main.text.level_info_1;
3654       char label_text[MAX_OUTPUT_LINESIZE + 1];
3655       int font_nr = pos->font;
3656       int max_len_label_text = getMaxTextLength(pos, font_nr);
3657
3658       if (pos->size != -1)
3659         max_len_label_text = pos->size;
3660
3661       strncpy(label_text, leveldir_current->name, max_len_label_text);
3662       label_text[max_len_label_text] = '\0';
3663
3664       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3665         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3666     }
3667
3668     return;
3669   }
3670
3671   // scroll preview level, if needed
3672   if (preview.anim_mode != ANIM_NONE &&
3673       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3674       DelayReached(&scroll_delay))
3675   {
3676     switch (scroll_direction)
3677     {
3678       case MV_LEFT:
3679         if (from_x > 0)
3680         {
3681           from_x -= preview.step_offset;
3682           from_x = (from_x < 0 ? 0 : from_x);
3683         }
3684         else
3685           scroll_direction = MV_UP;
3686         break;
3687
3688       case MV_RIGHT:
3689         if (from_x < level_xsize - preview.xsize)
3690         {
3691           from_x += preview.step_offset;
3692           from_x = (from_x > level_xsize - preview.xsize ?
3693                     level_xsize - preview.xsize : from_x);
3694         }
3695         else
3696           scroll_direction = MV_DOWN;
3697         break;
3698
3699       case MV_UP:
3700         if (from_y > 0)
3701         {
3702           from_y -= preview.step_offset;
3703           from_y = (from_y < 0 ? 0 : from_y);
3704         }
3705         else
3706           scroll_direction = MV_RIGHT;
3707         break;
3708
3709       case MV_DOWN:
3710         if (from_y < level_ysize - preview.ysize)
3711         {
3712           from_y += preview.step_offset;
3713           from_y = (from_y > level_ysize - preview.ysize ?
3714                     level_ysize - preview.ysize : from_y);
3715         }
3716         else
3717           scroll_direction = MV_LEFT;
3718         break;
3719
3720       default:
3721         break;
3722     }
3723
3724     DrawPreviewLevelPlayfield(from_x, from_y);
3725   }
3726
3727   // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3728   // redraw micro level label, if needed
3729   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3730       !strEqual(level.author, ANONYMOUS_NAME) &&
3731       !strEqual(level.author, leveldir_current->name) &&
3732       DelayReached(&label_delay))
3733   {
3734     int max_label_counter = 23;
3735
3736     if (leveldir_current->imported_from != NULL &&
3737         strlen(leveldir_current->imported_from) > 0)
3738       max_label_counter += 14;
3739     if (leveldir_current->imported_by != NULL &&
3740         strlen(leveldir_current->imported_by) > 0)
3741       max_label_counter += 14;
3742
3743     label_counter = (label_counter + 1) % max_label_counter;
3744     label_state = (label_counter >= 0 && label_counter <= 7 ?
3745                    MICROLABEL_LEVEL_NAME :
3746                    label_counter >= 9 && label_counter <= 12 ?
3747                    MICROLABEL_LEVEL_AUTHOR_HEAD :
3748                    label_counter >= 14 && label_counter <= 21 ?
3749                    MICROLABEL_LEVEL_AUTHOR :
3750                    label_counter >= 23 && label_counter <= 26 ?
3751                    MICROLABEL_IMPORTED_FROM_HEAD :
3752                    label_counter >= 28 && label_counter <= 35 ?
3753                    MICROLABEL_IMPORTED_FROM :
3754                    label_counter >= 37 && label_counter <= 40 ?
3755                    MICROLABEL_IMPORTED_BY_HEAD :
3756                    label_counter >= 42 && label_counter <= 49 ?
3757                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3758
3759     if (leveldir_current->imported_from == NULL &&
3760         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3761          label_state == MICROLABEL_IMPORTED_FROM))
3762       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3763                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3764
3765     DrawPreviewLevelLabel(label_state);
3766   }
3767 }
3768
3769 void DrawPreviewPlayers(void)
3770 {
3771   if (game_status != GAME_MODE_MAIN)
3772     return;
3773
3774   // do not draw preview players if level preview redefined, but players aren't
3775   if (preview.redefined && !menu.main.preview_players.redefined)
3776     return;
3777
3778   boolean player_found[MAX_PLAYERS];
3779   int num_players = 0;
3780   int i, x, y;
3781
3782   for (i = 0; i < MAX_PLAYERS; i++)
3783     player_found[i] = FALSE;
3784
3785   // check which players can be found in the level (simple approach)
3786   for (x = 0; x < lev_fieldx; x++)
3787   {
3788     for (y = 0; y < lev_fieldy; y++)
3789     {
3790       int element = level.field[x][y];
3791
3792       if (IS_PLAYER_ELEMENT(element))
3793       {
3794         int player_nr = GET_PLAYER_NR(element);
3795
3796         player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3797
3798         if (!player_found[player_nr])
3799           num_players++;
3800
3801         player_found[player_nr] = TRUE;
3802       }
3803     }
3804   }
3805
3806   struct TextPosInfo *pos = &menu.main.preview_players;
3807   int tile_size = pos->tile_size;
3808   int border_size = pos->border_size;
3809   int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3810   int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3811   int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3812   int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3813   int max_players_width  = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3814   int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3815   int all_players_width  = (num_players - 1) * player_xoffset + tile_size;
3816   int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3817   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3818   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3819   int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width,  pos->align);
3820   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3821
3822   // clear area in which the players will be drawn
3823   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3824                              max_players_width, max_players_height);
3825
3826   if (!network.enabled && !setup.team_mode)
3827     return;
3828
3829   // only draw players if level is suited for team mode
3830   if (num_players < 2)
3831     return;
3832
3833   // draw all players that were found in the level
3834   for (i = 0; i < MAX_PLAYERS; i++)
3835   {
3836     if (player_found[i])
3837     {
3838       int graphic = el2img(EL_PLAYER_1 + i);
3839
3840       DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3841
3842       xpos += player_xoffset;
3843       ypos += player_yoffset;
3844     }
3845   }
3846 }
3847
3848 static void PreparePreviewTileBitmap(void)
3849 {
3850   // check if special preview bitmap with level-specific colors should be created
3851   if (level.game_engine_type != GAME_ENGINE_TYPE_BD)
3852     return;
3853
3854   // use original sized bitmap (else reduced color palette is lost by downscaling)
3855   int original_tilesize = MAX(MINI_TILESIZE, preview.tile_size);
3856   int scale_down_factor = original_tilesize / preview.tile_size;
3857   Bitmap *src_bitmap;
3858   int src_x, src_y;
3859   int element_template = EL_BDX_GAME_GRAPHICS_COLOR_TEMPLATE;
3860   int graphic_template = el2preimg(element_template);
3861   int element_default = EL_BDX_ROCK;
3862   int graphic_default = el2preimg(element_default);
3863
3864   // create special preview bitmap and scale it down to preview tile size
3865   getSizedGraphicSource(graphic_template, 0, original_tilesize, &src_bitmap, &src_x, &src_y);
3866   PreparePreviewTileBitmap_BD(src_bitmap, scale_down_factor);
3867
3868   // force using special preview bitmap to replace original preview bitmap
3869   getSizedGraphicSource(graphic_default, 0, preview.tile_size, &src_bitmap, &src_x, &src_y);
3870   SetPreviewTileBitmapReference_BD(src_bitmap);
3871 }
3872
3873 void DrawPreviewLevelInitial(void)
3874 {
3875   PreparePreviewTileBitmap();   // only needed for native BD style levels
3876
3877   DrawPreviewLevelExt(TRUE);
3878   DrawPreviewPlayers();
3879 }
3880
3881 void DrawPreviewLevelAnimation(void)
3882 {
3883   DrawPreviewLevelExt(FALSE);
3884 }
3885
3886 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3887                               int border_size, int font_nr)
3888 {
3889   int graphic = el2img(EL_PLAYER_1 + player_nr);
3890   int font_height = getFontHeight(font_nr);
3891   int player_height = MAX(tile_size, font_height);
3892   int xoffset_text = tile_size + border_size;
3893   int yoffset_text    = (player_height - font_height) / 2;
3894   int yoffset_graphic = (player_height - tile_size) / 2;
3895   char *player_name = getNetworkPlayerName(player_nr + 1);
3896
3897   DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3898                               tile_size);
3899   DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3900 }
3901
3902 static void DrawNetworkPlayersExt(boolean force)
3903 {
3904   if (game_status != GAME_MODE_MAIN)
3905     return;
3906
3907   if (!network.connected && !force)
3908     return;
3909
3910   // do not draw network players if level preview redefined, but players aren't
3911   if (preview.redefined && !menu.main.network_players.redefined)
3912     return;
3913
3914   int num_players = 0;
3915   int i;
3916
3917   for (i = 0; i < MAX_PLAYERS; i++)
3918     if (stored_player[i].connected_network)
3919       num_players++;
3920
3921   struct TextPosInfo *pos = &menu.main.network_players;
3922   int tile_size = pos->tile_size;
3923   int border_size = pos->border_size;
3924   int xoffset_text = tile_size + border_size;
3925   int font_nr = pos->font;
3926   int font_width = getFontWidth(font_nr);
3927   int font_height = getFontHeight(font_nr);
3928   int player_height = MAX(tile_size, font_height);
3929   int player_yoffset = player_height + border_size;
3930   int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3931   int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3932   int all_players_height = num_players * player_yoffset - border_size;
3933   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3934   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3935   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3936
3937   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3938                              max_players_width, max_players_height);
3939
3940   // first draw local network player ...
3941   for (i = 0; i < MAX_PLAYERS; i++)
3942   {
3943     if (stored_player[i].connected_network &&
3944         stored_player[i].connected_locally)
3945     {
3946       char *player_name = getNetworkPlayerName(i + 1);
3947       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3948       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3949
3950       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3951
3952       ypos += player_yoffset;
3953     }
3954   }
3955
3956   // ... then draw all other network players
3957   for (i = 0; i < MAX_PLAYERS; i++)
3958   {
3959     if (stored_player[i].connected_network &&
3960         !stored_player[i].connected_locally)
3961     {
3962       char *player_name = getNetworkPlayerName(i + 1);
3963       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3964       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3965
3966       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3967
3968       ypos += player_yoffset;
3969     }
3970   }
3971 }
3972
3973 void DrawNetworkPlayers(void)
3974 {
3975   DrawNetworkPlayersExt(FALSE);
3976 }
3977
3978 void ClearNetworkPlayers(void)
3979 {
3980   DrawNetworkPlayersExt(TRUE);
3981 }
3982
3983 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3984                                     int graphic, int lx, int ly,
3985                                     int mask_mode)
3986 {
3987   int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3988
3989   if (mask_mode == USE_MASKING)
3990     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3991   else
3992     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3993 }
3994
3995 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3996                                   int graphic, int sync_frame, int mask_mode)
3997 {
3998   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3999
4000   if (mask_mode == USE_MASKING)
4001     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
4002   else
4003     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
4004 }
4005
4006 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
4007                                   int graphic, int sync_frame, int tilesize,
4008                                   int mask_mode)
4009 {
4010   int frame = getGraphicAnimationFrame(graphic, sync_frame);
4011
4012   if (mask_mode == USE_MASKING)
4013     DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
4014   else
4015     DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
4016 }
4017
4018 static void DrawGraphicAnimation(int x, int y, int graphic)
4019 {
4020   int lx = LEVELX(x), ly = LEVELY(y);
4021   int mask_mode = NO_MASKING;
4022
4023   if (!IN_SCR_FIELD(x, y))
4024     return;
4025
4026   if (game.use_masked_elements)
4027   {
4028     if (Tile[lx][ly] != EL_EMPTY)
4029     {
4030       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4031
4032       mask_mode = USE_MASKING;
4033     }
4034   }
4035
4036   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
4037                           graphic, lx, ly, mask_mode);
4038
4039   MarkTileDirty(x, y);
4040 }
4041
4042 void DrawFixedGraphicAnimation(int x, int y, int graphic)
4043 {
4044   int lx = LEVELX(x), ly = LEVELY(y);
4045   int mask_mode = NO_MASKING;
4046
4047   if (!IN_SCR_FIELD(x, y))
4048     return;
4049
4050   if (game.use_masked_elements)
4051   {
4052     if (Tile[lx][ly] != EL_EMPTY)
4053     {
4054       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4055
4056       mask_mode = USE_MASKING;
4057     }
4058   }
4059
4060   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
4061                           graphic, lx, ly, mask_mode);
4062
4063   MarkTileDirty(x, y);
4064 }
4065
4066 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4067 {
4068   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4069 }
4070
4071 void DrawLevelElementAnimation(int x, int y, int element)
4072 {
4073   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4074
4075   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4076 }
4077
4078 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4079 {
4080   int sx = SCREENX(x), sy = SCREENY(y);
4081
4082   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4083     return;
4084
4085   if (Tile[x][y] == EL_EMPTY)
4086     graphic = el2img(GfxElementEmpty[x][y]);
4087
4088   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4089     return;
4090
4091   if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4092     return;
4093
4094   DrawGraphicAnimation(sx, sy, graphic);
4095
4096 #if 1
4097   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4098     DrawLevelFieldCrumbled(x, y);
4099 #else
4100   if (GFX_CRUMBLED(Tile[x][y]))
4101     DrawLevelFieldCrumbled(x, y);
4102 #endif
4103 }
4104
4105 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4106 {
4107   int sx = SCREENX(x), sy = SCREENY(y);
4108   int graphic;
4109
4110   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4111     return;
4112
4113   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4114
4115   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4116     return;
4117
4118   DrawGraphicAnimation(sx, sy, graphic);
4119
4120   if (GFX_CRUMBLED(element))
4121     DrawLevelFieldCrumbled(x, y);
4122 }
4123
4124 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4125 {
4126   if (player->use_murphy)
4127   {
4128     // this works only because currently only one player can be "murphy" ...
4129     static int last_horizontal_dir = MV_LEFT;
4130     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4131
4132     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4133       last_horizontal_dir = move_dir;
4134
4135     if (graphic == IMG_SP_MURPHY)       // undefined => use special graphic
4136     {
4137       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4138
4139       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4140     }
4141
4142     return graphic;
4143   }
4144   else
4145     return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4146 }
4147
4148 static boolean equalGraphics(int graphic1, int graphic2)
4149 {
4150   struct GraphicInfo *g1 = &graphic_info[graphic1];
4151   struct GraphicInfo *g2 = &graphic_info[graphic2];
4152
4153   return (g1->bitmap      == g2->bitmap &&
4154           g1->src_x       == g2->src_x &&
4155           g1->src_y       == g2->src_y &&
4156           g1->anim_frames == g2->anim_frames &&
4157           g1->anim_delay  == g2->anim_delay &&
4158           g1->anim_mode   == g2->anim_mode);
4159 }
4160
4161 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4162
4163 enum
4164 {
4165   DRAW_PLAYER_STAGE_INIT = 0,
4166   DRAW_PLAYER_STAGE_LAST_FIELD,
4167   DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4168 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4169   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4170   DRAW_PLAYER_STAGE_PLAYER,
4171 #else
4172   DRAW_PLAYER_STAGE_PLAYER,
4173   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4174 #endif
4175   DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4176   DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4177
4178   NUM_DRAW_PLAYER_STAGES
4179 };
4180
4181 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4182 {
4183   static int static_last_player_graphic[MAX_PLAYERS];
4184   static int static_last_player_frame[MAX_PLAYERS];
4185   static boolean static_player_is_opaque[MAX_PLAYERS];
4186   static boolean draw_player[MAX_PLAYERS];
4187   int pnr = player->index_nr;
4188
4189   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4190   {
4191     static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4192     static_last_player_frame[pnr] = player->Frame;
4193     static_player_is_opaque[pnr] = FALSE;
4194
4195     draw_player[pnr] = TRUE;
4196   }
4197
4198   if (!draw_player[pnr])
4199     return;
4200
4201 #if DEBUG
4202   if (!IN_LEV_FIELD(player->jx, player->jy))
4203   {
4204     Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4205     Debug("draw:DrawPlayerExt", "This should never happen!");
4206
4207     draw_player[pnr] = FALSE;
4208
4209     return;
4210   }
4211 #endif
4212
4213   int last_player_graphic  = static_last_player_graphic[pnr];
4214   int last_player_frame    = static_last_player_frame[pnr];
4215   boolean player_is_opaque = static_player_is_opaque[pnr];
4216
4217   int jx = player->jx;
4218   int jy = player->jy;
4219   int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4220   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4221   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
4222   int last_jx = (player->is_moving ? jx - dx : jx);
4223   int last_jy = (player->is_moving ? jy - dy : jy);
4224   int next_jx = jx + dx;
4225   int next_jy = jy + dy;
4226   boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4227   int sx = SCREENX(jx);
4228   int sy = SCREENY(jy);
4229   int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4230   int syy = (move_dir == MV_UP   || move_dir == MV_DOWN  ? player->GfxPos : 0);
4231   int element = Tile[jx][jy];
4232   int last_element = Tile[last_jx][last_jy];
4233   int action = (player->is_pushing    ? ACTION_PUSHING         :
4234                 player->is_digging    ? ACTION_DIGGING         :
4235                 player->is_collecting ? ACTION_COLLECTING      :
4236                 player->is_moving     ? ACTION_MOVING          :
4237                 player->is_snapping   ? ACTION_SNAPPING        :
4238                 player->is_dropping   ? ACTION_DROPPING        :
4239                 player->is_waiting    ? player->action_waiting :
4240                 ACTION_DEFAULT);
4241
4242   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4243   {
4244     // ------------------------------------------------------------------------
4245     // initialize drawing the player
4246     // ------------------------------------------------------------------------
4247
4248     draw_player[pnr] = FALSE;
4249
4250     // GfxElement[][] is set to the element the player is digging or collecting;
4251     // remove also for off-screen player if the player is not moving anymore
4252     if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4253       GfxElement[jx][jy] = EL_UNDEFINED;
4254
4255     if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4256       return;
4257
4258     if (element == EL_EXPLOSION)
4259       return;
4260
4261     InitPlayerGfxAnimation(player, action, move_dir);
4262
4263     draw_player[pnr] = TRUE;
4264   }
4265   else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4266   {
4267     // ------------------------------------------------------------------------
4268     // draw things in the field the player is leaving, if needed
4269     // ------------------------------------------------------------------------
4270
4271     if (!IN_SCR_FIELD(sx, sy))
4272       draw_player[pnr] = FALSE;
4273
4274     if (!player->is_moving)
4275       return;
4276
4277     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4278     {
4279       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4280
4281       if (last_element == EL_DYNAMITE_ACTIVE ||
4282           last_element == EL_EM_DYNAMITE_ACTIVE ||
4283           last_element == EL_SP_DISK_RED_ACTIVE)
4284         DrawDynamite(last_jx, last_jy);
4285       else
4286         DrawLevelFieldThruMask(last_jx, last_jy);
4287     }
4288     else if (last_element == EL_DYNAMITE_ACTIVE ||
4289              last_element == EL_EM_DYNAMITE_ACTIVE ||
4290              last_element == EL_SP_DISK_RED_ACTIVE)
4291       DrawDynamite(last_jx, last_jy);
4292     else
4293       DrawLevelField(last_jx, last_jy);
4294   }
4295   else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4296   {
4297     // ------------------------------------------------------------------------
4298     // draw things behind the player, if needed
4299     // ------------------------------------------------------------------------
4300
4301     if (Back[jx][jy])
4302     {
4303       DrawLevelElement(jx, jy, Back[jx][jy]);
4304
4305       return;
4306     }
4307
4308     if (IS_ACTIVE_BOMB(element))
4309     {
4310       DrawLevelElement(jx, jy, EL_EMPTY);
4311
4312       return;
4313     }
4314
4315     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4316     {
4317       int old_element = GfxElement[jx][jy];
4318       int old_graphic = el_act_dir2img(old_element, action, move_dir);
4319       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4320
4321       if (GFX_CRUMBLED(old_element))
4322         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4323       else
4324         DrawScreenGraphic(sx, sy, old_graphic, frame);
4325
4326       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4327         static_player_is_opaque[pnr] = TRUE;
4328     }
4329     else
4330     {
4331       GfxElement[jx][jy] = EL_UNDEFINED;
4332
4333       // make sure that pushed elements are drawn with correct frame rate
4334       int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4335
4336       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4337         GfxFrame[jx][jy] = player->StepFrame;
4338
4339       DrawLevelField(jx, jy);
4340     }
4341   }
4342   else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4343   {
4344     // ------------------------------------------------------------------------
4345     // draw things the player is pushing, if needed
4346     // ------------------------------------------------------------------------
4347
4348     if (!player->is_pushing || !player->is_moving)
4349       return;
4350
4351     if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4352       return;
4353
4354     int gfx_frame = GfxFrame[jx][jy];
4355
4356     if (!IS_MOVING(jx, jy))             // push movement already finished
4357     {
4358       element = Tile[next_jx][next_jy];
4359       gfx_frame = GfxFrame[next_jx][next_jy];
4360     }
4361
4362     int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4363     int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4364     int frame = getGraphicAnimationFrame(graphic, sync_frame);
4365
4366     // draw background element under pushed element (like the Sokoban field)
4367     if (game.use_masked_pushing && IS_MOVING(jx, jy))
4368     {
4369       // this allows transparent pushing animation over non-black background
4370
4371       if (Back[jx][jy])
4372         DrawLevelElement(jx, jy, Back[jx][jy]);
4373       else
4374         DrawLevelElement(jx, jy, EL_EMPTY);
4375     }
4376
4377     if (Back[next_jx][next_jy])
4378       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4379     else
4380       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4381
4382     int px = SCREENX(jx), py = SCREENY(jy);
4383     int pxx = (TILEX - ABS(sxx)) * dx;
4384     int pyy = (TILEY - ABS(syy)) * dy;
4385
4386 #if 1
4387     // do not draw (EM style) pushing animation when pushing is finished
4388     // (two-tile animations usually do not contain start and end frame)
4389     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4390       DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4391     else
4392       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4393 #else
4394     // masked drawing is needed for EMC style (double) movement graphics
4395     // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4396     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4397 #endif
4398   }
4399   else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4400   {
4401     // ------------------------------------------------------------------------
4402     // draw player himself
4403     // ------------------------------------------------------------------------
4404
4405     int graphic = getPlayerGraphic(player, move_dir);
4406
4407     // in the case of changed player action or direction, prevent the current
4408     // animation frame from being restarted for identical animations
4409     if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4410       player->Frame = last_player_frame;
4411
4412     int frame = getGraphicAnimationFrame(graphic, player->Frame);
4413
4414     if (player_is_opaque)
4415       DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4416     else
4417       DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4418
4419     if (SHIELD_ON(player))
4420     {
4421       graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4422                  IMG_SHIELD_NORMAL_ACTIVE);
4423       frame = getGraphicAnimationFrame(graphic, -1);
4424
4425       DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4426     }
4427   }
4428   else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4429   {
4430     // ------------------------------------------------------------------------
4431     // draw things in front of player (active dynamite or dynabombs)
4432     // ------------------------------------------------------------------------
4433
4434     if (IS_ACTIVE_BOMB(element))
4435     {
4436       int graphic = el2img(element);
4437       int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4438
4439       if (game.emulation == EMU_SUPAPLEX)
4440         DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4441       else
4442         DrawGraphicThruMask(sx, sy, graphic, frame);
4443     }
4444
4445     if (player_is_moving && last_element == EL_EXPLOSION)
4446     {
4447       int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4448                      GfxElement[last_jx][last_jy] :  EL_EMPTY);
4449       int graphic = el_act2img(element, ACTION_EXPLODING);
4450       int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4451       int phase = ExplodePhase[last_jx][last_jy] - 1;
4452       int frame = getGraphicAnimationFrame(graphic, phase - delay);
4453
4454       if (phase >= delay)
4455         DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4456     }
4457   }
4458   else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4459   {
4460     // ------------------------------------------------------------------------
4461     // draw elements the player is just walking/passing through/under
4462     // ------------------------------------------------------------------------
4463
4464     if (player_is_moving)
4465     {
4466       // handle the field the player is leaving ...
4467       if (IS_ACCESSIBLE_INSIDE(last_element))
4468         DrawLevelField(last_jx, last_jy);
4469       else if (IS_ACCESSIBLE_UNDER(last_element))
4470         DrawLevelFieldThruMask(last_jx, last_jy);
4471     }
4472
4473     // do not redraw accessible elements if the player is just pushing them
4474     if (!player_is_moving || !player->is_pushing)
4475     {
4476       // ... and the field the player is entering
4477       if (IS_ACCESSIBLE_INSIDE(element))
4478         DrawLevelField(jx, jy);
4479       else if (IS_ACCESSIBLE_UNDER(element))
4480         DrawLevelFieldThruMask(jx, jy);
4481     }
4482
4483     MarkTileDirty(sx, sy);
4484   }
4485 }
4486
4487 void DrawPlayer(struct PlayerInfo *player)
4488 {
4489   int i;
4490
4491   for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4492     DrawPlayerExt(player, i);
4493 }
4494
4495 void DrawAllPlayers(void)
4496 {
4497   int i, j;
4498
4499   for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4500     for (j = 0; j < MAX_PLAYERS; j++)
4501       if (stored_player[j].active)
4502         DrawPlayerExt(&stored_player[j], i);
4503 }
4504
4505 void DrawPlayerField(int x, int y)
4506 {
4507   if (!IS_PLAYER(x, y))
4508     return;
4509
4510   DrawPlayer(PLAYERINFO(x, y));
4511 }
4512
4513 // ----------------------------------------------------------------------------
4514
4515 void WaitForEventToContinue(void)
4516 {
4517   boolean first_wait = TRUE;
4518   boolean still_wait = TRUE;
4519
4520   if (program.headless)
4521     return;
4522
4523   // simulate releasing mouse button over last gadget, if still pressed
4524   if (button_status)
4525     HandleGadgets(-1, -1, 0);
4526
4527   button_status = MB_RELEASED;
4528
4529   ClearEventQueue();
4530   ClearPlayerAction();
4531
4532   while (still_wait)
4533   {
4534     Event event;
4535
4536     if (NextValidEvent(&event))
4537     {
4538       switch (event.type)
4539       {
4540         case EVENT_BUTTONPRESS:
4541         case EVENT_FINGERPRESS:
4542           first_wait = FALSE;
4543           break;
4544
4545         case EVENT_BUTTONRELEASE:
4546         case EVENT_FINGERRELEASE:
4547           still_wait = first_wait;
4548           break;
4549
4550         case EVENT_KEYPRESS:
4551         case SDL_CONTROLLERBUTTONDOWN:
4552         case SDL_JOYBUTTONDOWN:
4553           still_wait = FALSE;
4554           break;
4555
4556         default:
4557           HandleOtherEvents(&event);
4558           break;
4559       }
4560     }
4561     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4562     {
4563       still_wait = FALSE;
4564     }
4565
4566     if (!PendingEvent())
4567       BackToFront();
4568   }
4569 }
4570
4571 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4572 {
4573   boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4574   int draw_buffer_last = GetDrawtoField();
4575   int width  = request.width;
4576   int height = request.height;
4577   int sx, sy;
4578   int result;
4579
4580   setRequestPosition(&sx, &sy, FALSE);
4581
4582   button_status = MB_RELEASED;
4583
4584   request_gadget_id = -1;
4585   result = -1;
4586
4587   while (result < 0)
4588   {
4589     if (game_ended)
4590     {
4591       SetDrawtoField(draw_buffer_game);
4592
4593       HandleGameActions();
4594
4595       SetDrawtoField(DRAW_TO_BACKBUFFER);
4596     }
4597
4598     if (PendingEvent())
4599     {
4600       Event event;
4601
4602       while (NextValidEvent(&event))
4603       {
4604         switch (event.type)
4605         {
4606           case EVENT_BUTTONPRESS:
4607           case EVENT_BUTTONRELEASE:
4608           case EVENT_MOTIONNOTIFY:
4609           {
4610             DrawBuffer *drawto_last = drawto;
4611             int mx, my;
4612
4613             if (event.type == EVENT_MOTIONNOTIFY)
4614             {
4615               if (!button_status)
4616                 continue;
4617
4618               motion_status = TRUE;
4619               mx = ((MotionEvent *) &event)->x;
4620               my = ((MotionEvent *) &event)->y;
4621             }
4622             else
4623             {
4624               motion_status = FALSE;
4625               mx = ((ButtonEvent *) &event)->x;
4626               my = ((ButtonEvent *) &event)->y;
4627               if (event.type == EVENT_BUTTONPRESS)
4628                 button_status = ((ButtonEvent *) &event)->button;
4629               else
4630                 button_status = MB_RELEASED;
4631             }
4632
4633             if (global.use_envelope_request)
4634             {
4635               // draw changed button states to temporary bitmap
4636               drawto = bitmap_db_store_1;
4637             }
4638
4639             // this sets 'request_gadget_id'
4640             HandleGadgets(mx, my, button_status);
4641
4642             if (global.use_envelope_request)
4643             {
4644               // restore pointer to drawing buffer
4645               drawto = drawto_last;
4646
4647               // prepare complete envelope request from temporary bitmap
4648               PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4649                                              width, height);
4650             }
4651
4652             switch (request_gadget_id)
4653             {
4654               case TOOL_CTRL_ID_YES:
4655               case TOOL_CTRL_ID_TOUCH_YES:
4656                 result = TRUE;
4657                 break;
4658               case TOOL_CTRL_ID_NO:
4659               case TOOL_CTRL_ID_TOUCH_NO:
4660                 result = FALSE;
4661                 break;
4662               case TOOL_CTRL_ID_CONFIRM:
4663               case TOOL_CTRL_ID_TOUCH_CONFIRM:
4664                 result = TRUE | FALSE;
4665                 break;
4666
4667               case TOOL_CTRL_ID_PLAYER_1:
4668                 result = 1;
4669                 break;
4670               case TOOL_CTRL_ID_PLAYER_2:
4671                 result = 2;
4672                 break;
4673               case TOOL_CTRL_ID_PLAYER_3:
4674                 result = 3;
4675                 break;
4676               case TOOL_CTRL_ID_PLAYER_4:
4677                 result = 4;
4678                 break;
4679
4680               default:
4681                 break;
4682             }
4683
4684             // only needed to handle clickable pointer animations here
4685             HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4686
4687             break;
4688           }
4689
4690           case SDL_WINDOWEVENT:
4691             HandleWindowEvent((WindowEvent *) &event);
4692             break;
4693
4694           case SDL_APP_WILLENTERBACKGROUND:
4695           case SDL_APP_DIDENTERBACKGROUND:
4696           case SDL_APP_WILLENTERFOREGROUND:
4697           case SDL_APP_DIDENTERFOREGROUND:
4698             HandlePauseResumeEvent((PauseResumeEvent *) &event);
4699             break;
4700
4701           case EVENT_KEYPRESS:
4702           {
4703             Key key = GetEventKey((KeyEvent *)&event);
4704
4705             switch (key)
4706             {
4707               case KSYM_space:
4708                 if (req_state & REQ_CONFIRM)
4709                   result = 1;
4710                 break;
4711
4712               case KSYM_Return:
4713               case KSYM_y:
4714               case KSYM_Y:
4715               case KSYM_Select:
4716               case KSYM_Menu:
4717 #if defined(KSYM_Rewind)
4718               case KSYM_Rewind:         // for Amazon Fire TV remote
4719 #endif
4720                 result = 1;
4721                 break;
4722
4723               case KSYM_Escape:
4724               case KSYM_n:
4725               case KSYM_N:
4726               case KSYM_Back:
4727 #if defined(KSYM_FastForward)
4728               case KSYM_FastForward:    // for Amazon Fire TV remote
4729 #endif
4730                 result = 0;
4731                 break;
4732
4733               default:
4734                 HandleKeysDebug(key, KEY_PRESSED);
4735                 break;
4736             }
4737
4738             if (req_state & REQ_PLAYER)
4739             {
4740               int old_player_nr = setup.network_player_nr;
4741
4742               if (result != -1)
4743                 result = old_player_nr + 1;
4744
4745               switch (key)
4746               {
4747                 case KSYM_space:
4748                   result = old_player_nr + 1;
4749                   break;
4750
4751                 case KSYM_Up:
4752                 case KSYM_1:
4753                   result = 1;
4754                   break;
4755
4756                 case KSYM_Right:
4757                 case KSYM_2:
4758                   result = 2;
4759                   break;
4760
4761                 case KSYM_Down:
4762                 case KSYM_3:
4763                   result = 3;
4764                   break;
4765
4766                 case KSYM_Left:
4767                 case KSYM_4:
4768                   result = 4;
4769                   break;
4770
4771                 default:
4772                   break;
4773               }
4774             }
4775
4776             break;
4777           }
4778
4779           case EVENT_FINGERRELEASE:
4780           case EVENT_KEYRELEASE:
4781             ClearPlayerAction();
4782             break;
4783
4784           case SDL_CONTROLLERBUTTONDOWN:
4785             switch (event.cbutton.button)
4786             {
4787               case SDL_CONTROLLER_BUTTON_A:
4788               case SDL_CONTROLLER_BUTTON_X:
4789               case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4790               case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4791                 result = 1;
4792                 break;
4793
4794               case SDL_CONTROLLER_BUTTON_B:
4795               case SDL_CONTROLLER_BUTTON_Y:
4796               case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4797               case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4798               case SDL_CONTROLLER_BUTTON_BACK:
4799                 result = 0;
4800                 break;
4801             }
4802
4803             if (req_state & REQ_PLAYER)
4804             {
4805               int old_player_nr = setup.network_player_nr;
4806
4807               if (result != -1)
4808                 result = old_player_nr + 1;
4809
4810               switch (event.cbutton.button)
4811               {
4812                 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4813                 case SDL_CONTROLLER_BUTTON_Y:
4814                   result = 1;
4815                   break;
4816
4817                 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4818                 case SDL_CONTROLLER_BUTTON_B:
4819                   result = 2;
4820                   break;
4821
4822                 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4823                 case SDL_CONTROLLER_BUTTON_A:
4824                   result = 3;
4825                   break;
4826
4827                 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4828                 case SDL_CONTROLLER_BUTTON_X:
4829                   result = 4;
4830                   break;
4831
4832                 default:
4833                   break;
4834               }
4835             }
4836
4837             break;
4838
4839           case SDL_CONTROLLERBUTTONUP:
4840             HandleJoystickEvent(&event);
4841             ClearPlayerAction();
4842             break;
4843
4844           default:
4845             HandleOtherEvents(&event);
4846             break;
4847         }
4848       }
4849     }
4850     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4851     {
4852       int joy = AnyJoystick();
4853
4854       if (joy & JOY_BUTTON_1)
4855         result = 1;
4856       else if (joy & JOY_BUTTON_2)
4857         result = 0;
4858     }
4859     else if (AnyJoystick())
4860     {
4861       int joy = AnyJoystick();
4862
4863       if (req_state & REQ_PLAYER)
4864       {
4865         if (joy & JOY_UP)
4866           result = 1;
4867         else if (joy & JOY_RIGHT)
4868           result = 2;
4869         else if (joy & JOY_DOWN)
4870           result = 3;
4871         else if (joy & JOY_LEFT)
4872           result = 4;
4873       }
4874     }
4875
4876     BackToFront();
4877   }
4878
4879   SetDrawtoField(draw_buffer_last);
4880
4881   return result;
4882 }
4883
4884 static void DoRequestBefore(void)
4885 {
4886   boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4887
4888   // when showing request dialog after game ended, deactivate game panel
4889   if (game_ended)
4890     game.panel.active = FALSE;
4891
4892   if (game_status == GAME_MODE_PLAYING)
4893     BlitScreenToBitmap(backbuffer);
4894
4895   // disable deactivated drawing when quick-loading level tape recording
4896   if (tape.playing && tape.deactivate_display)
4897     TapeDeactivateDisplayOff(TRUE);
4898
4899   SetMouseCursor(CURSOR_DEFAULT);
4900
4901   // pause network game while waiting for request to answer
4902   if (network.enabled &&
4903       game_status == GAME_MODE_PLAYING &&
4904       !game.all_players_gone)
4905     SendToServer_PausePlaying();
4906
4907   // simulate releasing mouse button over last gadget, if still pressed
4908   if (button_status)
4909     HandleGadgets(-1, -1, 0);
4910
4911   UnmapAllGadgets();
4912 }
4913
4914 static void DoRequestAfter(void)
4915 {
4916   RemapAllGadgets();
4917
4918   if (game_status == GAME_MODE_PLAYING)
4919   {
4920     SetPanelBackground();
4921     SetDrawBackgroundMask(REDRAW_DOOR_1);
4922   }
4923   else
4924   {
4925     SetDrawBackgroundMask(REDRAW_FIELD);
4926   }
4927
4928   // continue network game after request
4929   if (network.enabled &&
4930       game_status == GAME_MODE_PLAYING &&
4931       !game.all_players_gone)
4932     SendToServer_ContinuePlaying();
4933
4934   // restore deactivated drawing when quick-loading level tape recording
4935   if (tape.playing && tape.deactivate_display)
4936     TapeDeactivateDisplayOn();
4937 }
4938
4939 static void setRequestDoorTextProperties(char *text,
4940                                          int text_spacing,
4941                                          int line_spacing,
4942                                          int *set_font_nr,
4943                                          int *set_max_lines,
4944                                          int *set_max_line_length)
4945 {
4946   struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4947   struct TextPosInfo *pos = &request.button.confirm;
4948   int button_ypos = pos->y;
4949   int font_nr = FONT_TEXT_2;
4950   int font_width = getFontWidth(font_nr);
4951   int font_height = getFontHeight(font_nr);
4952   int line_height = font_height + line_spacing;
4953   int max_text_width  = vp_door_1->width;
4954   int max_text_height = button_ypos - 2 * text_spacing;
4955   int max_line_length = max_text_width  / font_width;
4956   int max_lines       = max_text_height / line_height;
4957
4958   if (maxWordLengthInRequestString(text) > max_line_length)
4959   {
4960     font_nr = FONT_TEXT_1;
4961     font_width = getFontWidth(font_nr);
4962     max_line_length = max_text_width  / font_width;
4963   }
4964
4965   *set_font_nr = font_nr;
4966   *set_max_lines = max_lines;
4967   *set_max_line_length = max_line_length;
4968 }
4969
4970 static void DrawRequestDoorText(char *text)
4971 {
4972   char *text_ptr = text;
4973   int text_spacing = 8;
4974   int line_spacing = 2;
4975   int max_request_lines;
4976   int max_request_line_len;
4977   int font_nr;
4978   int ty;
4979
4980   // force DOOR font inside door area
4981   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4982
4983   setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4984                                &max_request_lines, &max_request_line_len);
4985
4986   for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4987   {
4988     char text_line[max_request_line_len + 1];
4989     int tx, tl, tc = 0;
4990
4991     if (!*text_ptr)
4992       break;
4993
4994     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4995     {
4996       tc = *(text_ptr + tx);
4997       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4998         break;
4999     }
5000
5001     if ((tc == '?' || tc == '!') && tl == 0)
5002       tl = 1;
5003
5004     if (!tl)
5005     { 
5006       text_ptr++; 
5007       ty--; 
5008       continue; 
5009     }
5010
5011     strncpy(text_line, text_ptr, tl);
5012     text_line[tl] = 0;
5013
5014     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
5015              DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
5016              text_line, font_nr);
5017
5018     text_ptr += tl + (tc == ' ' ? 1 : 0);
5019   }
5020
5021   ResetFontStatus();
5022 }
5023
5024 static int RequestDoor(char *text, unsigned int req_state)
5025 {
5026   unsigned int old_door_state = GetDoorState();
5027   int draw_buffer_last = GetDrawtoField();
5028   int result;
5029
5030   if (old_door_state & DOOR_OPEN_1)
5031   {
5032     CloseDoor(DOOR_CLOSE_1);
5033
5034     // save old door content
5035     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5036                0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
5037   }
5038
5039   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5040   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5041
5042   // clear door drawing field
5043   DrawBackground(DX, DY, DXSIZE, DYSIZE);
5044
5045   // write text for request
5046   DrawRequestDoorText(text);
5047
5048   MapToolButtons(req_state);
5049
5050   // copy request gadgets to door backbuffer
5051   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
5052
5053   OpenDoor(DOOR_OPEN_1);
5054
5055   // ---------- handle request buttons ----------
5056   result = RequestHandleEvents(req_state, draw_buffer_last);
5057
5058   UnmapToolButtons();
5059
5060   if (!(req_state & REQ_STAY_OPEN))
5061   {
5062     CloseDoor(DOOR_CLOSE_1);
5063
5064     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
5065         (req_state & REQ_REOPEN))
5066       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5067   }
5068
5069   return result;
5070 }
5071
5072 static int RequestEnvelope(char *text, unsigned int req_state)
5073 {
5074   int draw_buffer_last = GetDrawtoField();
5075   int result;
5076
5077   DrawEnvelopeRequest(text, req_state);
5078   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5079
5080   // ---------- handle request buttons ----------
5081   result = RequestHandleEvents(req_state, draw_buffer_last);
5082
5083   UnmapToolButtons();
5084
5085   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5086
5087   return result;
5088 }
5089
5090 int Request(char *text, unsigned int req_state)
5091 {
5092   boolean overlay_enabled = GetOverlayEnabled();
5093   int result;
5094
5095   game.request_active = TRUE;
5096
5097   SetOverlayEnabled(FALSE);
5098
5099   DoRequestBefore();
5100
5101   if (global.use_envelope_request)
5102     result = RequestEnvelope(text, req_state);
5103   else
5104     result = RequestDoor(text, req_state);
5105
5106   DoRequestAfter();
5107
5108   SetOverlayEnabled(overlay_enabled);
5109
5110   game.request_active = FALSE;
5111
5112   return result;
5113 }
5114
5115 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5116 {
5117   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5118   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5119   int compare_result;
5120
5121   if (dpo1->sort_priority != dpo2->sort_priority)
5122     compare_result = dpo1->sort_priority - dpo2->sort_priority;
5123   else
5124     compare_result = dpo1->nr - dpo2->nr;
5125
5126   return compare_result;
5127 }
5128
5129 void InitGraphicCompatibilityInfo_Doors(void)
5130 {
5131   struct
5132   {
5133     int door_token;
5134     int part_1, part_8;
5135     struct DoorInfo *door;
5136   }
5137   doors[] =
5138   {
5139     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
5140     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
5141
5142     { -1,       -1,                     -1,                     NULL    }
5143   };
5144   struct Rect door_rect_list[] =
5145   {
5146     { DX, DY, DXSIZE, DYSIZE },
5147     { VX, VY, VXSIZE, VYSIZE }
5148   };
5149   int i, j;
5150
5151   for (i = 0; doors[i].door_token != -1; i++)
5152   {
5153     int door_token = doors[i].door_token;
5154     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5155     int part_1 = doors[i].part_1;
5156     int part_8 = doors[i].part_8;
5157     int part_2 = part_1 + 1;
5158     int part_3 = part_1 + 2;
5159     struct DoorInfo *door = doors[i].door;
5160     struct Rect *door_rect = &door_rect_list[door_index];
5161     boolean door_gfx_redefined = FALSE;
5162
5163     // check if any door part graphic definitions have been redefined
5164
5165     for (j = 0; door_part_controls[j].door_token != -1; j++)
5166     {
5167       struct DoorPartControlInfo *dpc = &door_part_controls[j];
5168       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5169
5170       if (dpc->door_token == door_token && fi->redefined)
5171         door_gfx_redefined = TRUE;
5172     }
5173
5174     // check for old-style door graphic/animation modifications
5175
5176     if (!door_gfx_redefined)
5177     {
5178       if (door->anim_mode & ANIM_STATIC_PANEL)
5179       {
5180         door->panel.step_xoffset = 0;
5181         door->panel.step_yoffset = 0;
5182       }
5183
5184       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5185       {
5186         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5187         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5188         int num_door_steps, num_panel_steps;
5189
5190         // remove door part graphics other than the two default wings
5191
5192         for (j = 0; door_part_controls[j].door_token != -1; j++)
5193         {
5194           struct DoorPartControlInfo *dpc = &door_part_controls[j];
5195           struct GraphicInfo *g = &graphic_info[dpc->graphic];
5196
5197           if (dpc->graphic >= part_3 &&
5198               dpc->graphic <= part_8)
5199             g->bitmap = NULL;
5200         }
5201
5202         // set graphics and screen positions of the default wings
5203
5204         g_part_1->width  = door_rect->width;
5205         g_part_1->height = door_rect->height;
5206         g_part_2->width  = door_rect->width;
5207         g_part_2->height = door_rect->height;
5208         g_part_2->src_x = door_rect->width;
5209         g_part_2->src_y = g_part_1->src_y;
5210
5211         door->part_2.x = door->part_1.x;
5212         door->part_2.y = door->part_1.y;
5213
5214         if (door->width != -1)
5215         {
5216           g_part_1->width = door->width;
5217           g_part_2->width = door->width;
5218
5219           // special treatment for graphics and screen position of right wing
5220           g_part_2->src_x += door_rect->width - door->width;
5221           door->part_2.x  += door_rect->width - door->width;
5222         }
5223
5224         if (door->height != -1)
5225         {
5226           g_part_1->height = door->height;
5227           g_part_2->height = door->height;
5228
5229           // special treatment for graphics and screen position of bottom wing
5230           g_part_2->src_y += door_rect->height - door->height;
5231           door->part_2.y  += door_rect->height - door->height;
5232         }
5233
5234         // set animation delays for the default wings and panels
5235
5236         door->part_1.step_delay = door->step_delay;
5237         door->part_2.step_delay = door->step_delay;
5238         door->panel.step_delay  = door->step_delay;
5239
5240         // set animation draw order for the default wings
5241
5242         door->part_1.sort_priority = 2; // draw left wing over ...
5243         door->part_2.sort_priority = 1; //          ... right wing
5244
5245         // set animation draw offset for the default wings
5246
5247         if (door->anim_mode & ANIM_HORIZONTAL)
5248         {
5249           door->part_1.step_xoffset = door->step_offset;
5250           door->part_1.step_yoffset = 0;
5251           door->part_2.step_xoffset = door->step_offset * -1;
5252           door->part_2.step_yoffset = 0;
5253
5254           num_door_steps = g_part_1->width / door->step_offset;
5255         }
5256         else    // ANIM_VERTICAL
5257         {
5258           door->part_1.step_xoffset = 0;
5259           door->part_1.step_yoffset = door->step_offset;
5260           door->part_2.step_xoffset = 0;
5261           door->part_2.step_yoffset = door->step_offset * -1;
5262
5263           num_door_steps = g_part_1->height / door->step_offset;
5264         }
5265
5266         // set animation draw offset for the default panels
5267
5268         if (door->step_offset > 1)
5269         {
5270           num_panel_steps = 2 * door_rect->height / door->step_offset;
5271           door->panel.start_step = num_panel_steps - num_door_steps;
5272           door->panel.start_step_closing = door->panel.start_step;
5273         }
5274         else
5275         {
5276           num_panel_steps = door_rect->height / door->step_offset;
5277           door->panel.start_step = num_panel_steps - num_door_steps / 2;
5278           door->panel.start_step_closing = door->panel.start_step;
5279           door->panel.step_delay *= 2;
5280         }
5281       }
5282     }
5283   }
5284 }
5285
5286 void InitDoors(void)
5287 {
5288   int i;
5289
5290   for (i = 0; door_part_controls[i].door_token != -1; i++)
5291   {
5292     struct DoorPartControlInfo *dpc = &door_part_controls[i];
5293     struct DoorPartOrderInfo *dpo = &door_part_order[i];
5294
5295     // initialize "start_step_opening" and "start_step_closing", if needed
5296     if (dpc->pos->start_step_opening == 0 &&
5297         dpc->pos->start_step_closing == 0)
5298     {
5299       // dpc->pos->start_step_opening = dpc->pos->start_step;
5300       dpc->pos->start_step_closing = dpc->pos->start_step;
5301     }
5302
5303     // fill structure for door part draw order (sorted below)
5304     dpo->nr = i;
5305     dpo->sort_priority = dpc->pos->sort_priority;
5306   }
5307
5308   // sort door part controls according to sort_priority and graphic number
5309   qsort(door_part_order, MAX_DOOR_PARTS,
5310         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5311 }
5312
5313 unsigned int OpenDoor(unsigned int door_state)
5314 {
5315   if (door_state & DOOR_COPY_BACK)
5316   {
5317     if (door_state & DOOR_OPEN_1)
5318       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5319                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5320
5321     if (door_state & DOOR_OPEN_2)
5322       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5323                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5324
5325     door_state &= ~DOOR_COPY_BACK;
5326   }
5327
5328   return MoveDoor(door_state);
5329 }
5330
5331 unsigned int CloseDoor(unsigned int door_state)
5332 {
5333   unsigned int old_door_state = GetDoorState();
5334
5335   if (!(door_state & DOOR_NO_COPY_BACK))
5336   {
5337     if (old_door_state & DOOR_OPEN_1)
5338       BlitBitmap(backbuffer, bitmap_db_door_1,
5339                  DX, DY, DXSIZE, DYSIZE, 0, 0);
5340
5341     if (old_door_state & DOOR_OPEN_2)
5342       BlitBitmap(backbuffer, bitmap_db_door_2,
5343                  VX, VY, VXSIZE, VYSIZE, 0, 0);
5344
5345     door_state &= ~DOOR_NO_COPY_BACK;
5346   }
5347
5348   return MoveDoor(door_state);
5349 }
5350
5351 unsigned int GetDoorState(void)
5352 {
5353   return MoveDoor(DOOR_GET_STATE);
5354 }
5355
5356 unsigned int SetDoorState(unsigned int door_state)
5357 {
5358   return MoveDoor(door_state | DOOR_SET_STATE);
5359 }
5360
5361 static int euclid(int a, int b)
5362 {
5363   return (b ? euclid(b, a % b) : a);
5364 }
5365
5366 unsigned int MoveDoor(unsigned int door_state)
5367 {
5368   struct Rect door_rect_list[] =
5369   {
5370     { DX, DY, DXSIZE, DYSIZE },
5371     { VX, VY, VXSIZE, VYSIZE }
5372   };
5373   static int door1 = DOOR_CLOSE_1;
5374   static int door2 = DOOR_CLOSE_2;
5375   DelayCounter door_delay = { 0 };
5376   int i;
5377
5378   if (door_state == DOOR_GET_STATE)
5379     return (door1 | door2);
5380
5381   if (door_state & DOOR_SET_STATE)
5382   {
5383     if (door_state & DOOR_ACTION_1)
5384       door1 = door_state & DOOR_ACTION_1;
5385     if (door_state & DOOR_ACTION_2)
5386       door2 = door_state & DOOR_ACTION_2;
5387
5388     return (door1 | door2);
5389   }
5390
5391   if (!(door_state & DOOR_FORCE_REDRAW))
5392   {
5393     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5394       door_state &= ~DOOR_OPEN_1;
5395     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5396       door_state &= ~DOOR_CLOSE_1;
5397     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5398       door_state &= ~DOOR_OPEN_2;
5399     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5400       door_state &= ~DOOR_CLOSE_2;
5401   }
5402
5403   if (global.autoplay_leveldir)
5404   {
5405     door_state |= DOOR_NO_DELAY;
5406     door_state &= ~DOOR_CLOSE_ALL;
5407   }
5408
5409   if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5410     door_state |= DOOR_NO_DELAY;
5411
5412   if (door_state & DOOR_ACTION)
5413   {
5414     boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5415     boolean door_panel_drawn[NUM_DOORS];
5416     boolean panel_has_doors[NUM_DOORS];
5417     boolean door_part_skip[MAX_DOOR_PARTS];
5418     boolean door_part_done[MAX_DOOR_PARTS];
5419     boolean door_part_done_all;
5420     int num_steps[MAX_DOOR_PARTS];
5421     int max_move_delay = 0;     // delay for complete animations of all doors
5422     int max_step_delay = 0;     // delay (ms) between two animation frames
5423     int num_move_steps = 0;     // number of animation steps for all doors
5424     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
5425     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
5426     int start = 0;
5427     int k;
5428
5429     for (i = 0; i < NUM_DOORS; i++)
5430       panel_has_doors[i] = FALSE;
5431
5432     for (i = 0; i < MAX_DOOR_PARTS; i++)
5433     {
5434       struct DoorPartControlInfo *dpc = &door_part_controls[i];
5435       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5436       int door_token = dpc->door_token;
5437
5438       door_part_done[i] = FALSE;
5439       door_part_skip[i] = (!(door_state & door_token) ||
5440                            !g->bitmap);
5441     }
5442
5443     for (i = 0; i < MAX_DOOR_PARTS; i++)
5444     {
5445       int nr = door_part_order[i].nr;
5446       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5447       struct DoorPartPosInfo *pos = dpc->pos;
5448       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5449       int door_token = dpc->door_token;
5450       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5451       boolean is_panel = DOOR_PART_IS_PANEL(nr);
5452       int step_xoffset = ABS(pos->step_xoffset);
5453       int step_yoffset = ABS(pos->step_yoffset);
5454       int step_delay = pos->step_delay;
5455       int current_door_state = door_state & door_token;
5456       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5457       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5458       boolean part_opening = (is_panel ? door_closing : door_opening);
5459       int start_step = (part_opening ? pos->start_step_opening :
5460                         pos->start_step_closing);
5461       float move_xsize = (step_xoffset ? g->width  : 0);
5462       float move_ysize = (step_yoffset ? g->height : 0);
5463       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5464       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5465       int move_steps = (move_xsteps && move_ysteps ?
5466                         MIN(move_xsteps, move_ysteps) :
5467                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
5468       int move_delay = move_steps * step_delay;
5469
5470       if (door_part_skip[nr])
5471         continue;
5472
5473       max_move_delay = MAX(max_move_delay, move_delay);
5474       max_step_delay = (max_step_delay == 0 ? step_delay :
5475                         euclid(max_step_delay, step_delay));
5476       num_steps[nr] = move_steps;
5477
5478       if (!is_panel)
5479       {
5480         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5481
5482         panel_has_doors[door_index] = TRUE;
5483       }
5484     }
5485
5486     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
5487
5488     num_move_steps = max_move_delay / max_step_delay;
5489     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5490
5491     door_delay.value = max_step_delay;
5492
5493     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5494     {
5495       start = num_move_steps - 1;
5496     }
5497     else
5498     {
5499       // opening door sound has priority over simultaneously closing door
5500       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5501       {
5502         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5503
5504         if (door_state & DOOR_OPEN_1)
5505           PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5506         if (door_state & DOOR_OPEN_2)
5507           PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5508       }
5509       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5510       {
5511         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5512
5513         if (door_state & DOOR_CLOSE_1)
5514           PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5515         if (door_state & DOOR_CLOSE_2)
5516           PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5517       }
5518     }
5519
5520     SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5521
5522     game.any_door_active = TRUE;
5523
5524     for (k = start; k < num_move_steps; k++)
5525     {
5526       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
5527
5528       door_part_done_all = TRUE;
5529
5530       for (i = 0; i < NUM_DOORS; i++)
5531         door_panel_drawn[i] = FALSE;
5532
5533       for (i = 0; i < MAX_DOOR_PARTS; i++)
5534       {
5535         int nr = door_part_order[i].nr;
5536         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5537         struct DoorPartPosInfo *pos = dpc->pos;
5538         struct GraphicInfo *g = &graphic_info[dpc->graphic];
5539         int door_token = dpc->door_token;
5540         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5541         boolean is_panel = DOOR_PART_IS_PANEL(nr);
5542         boolean is_panel_and_door_has_closed = FALSE;
5543         struct Rect *door_rect = &door_rect_list[door_index];
5544         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5545                                   bitmap_db_door_2);
5546         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5547         int current_door_state = door_state & door_token;
5548         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5549         boolean door_closing = !door_opening;
5550         boolean part_opening = (is_panel ? door_closing : door_opening);
5551         boolean part_closing = !part_opening;
5552         int start_step = (part_opening ? pos->start_step_opening :
5553                           pos->start_step_closing);
5554         int step_delay = pos->step_delay;
5555         int step_factor = step_delay / max_step_delay;
5556         int k1 = (step_factor ? k / step_factor + 1 : k);
5557         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5558         int kk = MAX(0, k2);
5559         int g_src_x = 0;
5560         int g_src_y = 0;
5561         int src_x, src_y, src_xx, src_yy;
5562         int dst_x, dst_y, dst_xx, dst_yy;
5563         int width, height;
5564
5565         if (door_part_skip[nr])
5566           continue;
5567
5568         if (!(door_state & door_token))
5569           continue;
5570
5571         if (!g->bitmap)
5572           continue;
5573
5574         if (!is_panel)
5575         {
5576           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5577           int kk_door = MAX(0, k2_door);
5578           int sync_frame = kk_door * door_delay.value;
5579           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5580
5581           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5582                                 &g_src_x, &g_src_y);
5583         }
5584
5585         // draw door panel
5586
5587         if (!door_panel_drawn[door_index])
5588         {
5589           ClearRectangleOnBackground(drawto, door_rect->x, door_rect->y,
5590                                      door_rect->width, door_rect->height);
5591
5592           door_panel_drawn[door_index] = TRUE;
5593         }
5594
5595         // draw opening or closing door parts
5596
5597         if (pos->step_xoffset < 0)      // door part on right side
5598         {
5599           src_xx = 0;
5600           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5601           width = g->width;
5602
5603           if (dst_xx + width > door_rect->width)
5604             width = door_rect->width - dst_xx;
5605         }
5606         else                            // door part on left side
5607         {
5608           src_xx = 0;
5609           dst_xx = pos->x - kk * pos->step_xoffset;
5610
5611           if (dst_xx < 0)
5612           {
5613             src_xx = ABS(dst_xx);
5614             dst_xx = 0;
5615           }
5616
5617           width = g->width - src_xx;
5618
5619           if (width > door_rect->width)
5620             width = door_rect->width;
5621
5622           // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5623         }
5624
5625         if (pos->step_yoffset < 0)      // door part on bottom side
5626         {
5627           src_yy = 0;
5628           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5629           height = g->height;
5630
5631           if (dst_yy + height > door_rect->height)
5632             height = door_rect->height - dst_yy;
5633         }
5634         else                            // door part on top side
5635         {
5636           src_yy = 0;
5637           dst_yy = pos->y - kk * pos->step_yoffset;
5638
5639           if (dst_yy < 0)
5640           {
5641             src_yy = ABS(dst_yy);
5642             dst_yy = 0;
5643           }
5644
5645           height = g->height - src_yy;
5646         }
5647
5648         src_x = g_src_x + src_xx;
5649         src_y = g_src_y + src_yy;
5650
5651         dst_x = door_rect->x + dst_xx;
5652         dst_y = door_rect->y + dst_yy;
5653
5654         is_panel_and_door_has_closed =
5655           (is_panel &&
5656            door_closing &&
5657            panel_has_doors[door_index] &&
5658            k >= num_move_steps_doors_only - 1);
5659
5660         if (width  >= 0 && width  <= g->width &&
5661             height >= 0 && height <= g->height &&
5662             !is_panel_and_door_has_closed)
5663         {
5664           if (is_panel || !pos->draw_masked)
5665             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5666                        dst_x, dst_y);
5667           else
5668             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5669                              dst_x, dst_y);
5670         }
5671
5672         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5673
5674         if ((part_opening && (width < 0         || height < 0)) ||
5675             (part_closing && (width >= g->width && height >= g->height)))
5676           door_part_done[nr] = TRUE;
5677
5678         // continue door part animations, but not panel after door has closed
5679         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5680           door_part_done_all = FALSE;
5681       }
5682
5683       if (!(door_state & DOOR_NO_DELAY))
5684       {
5685         if (game_ended)
5686           HandleGameActions();
5687
5688         BackToFront();
5689
5690         SkipUntilDelayReached(&door_delay, &k, last_frame);
5691
5692         // prevent OS (Windows) from complaining about program not responding
5693         CheckQuitEvent();
5694       }
5695
5696       if (door_part_done_all)
5697         break;
5698     }
5699
5700     if (!(door_state & DOOR_NO_DELAY))
5701     {
5702       // wait for specified door action post delay
5703       if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5704         door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5705       else if (door_state & DOOR_ACTION_1)
5706         door_delay.value = door_1.post_delay;
5707       else if (door_state & DOOR_ACTION_2)
5708         door_delay.value = door_2.post_delay;
5709
5710       while (!DelayReached(&door_delay))
5711       {
5712         if (game_ended)
5713           HandleGameActions();
5714
5715         BackToFront();
5716       }
5717     }
5718
5719     game.any_door_active = FALSE;
5720   }
5721
5722   if (door_state & DOOR_ACTION_1)
5723     door1 = door_state & DOOR_ACTION_1;
5724   if (door_state & DOOR_ACTION_2)
5725     door2 = door_state & DOOR_ACTION_2;
5726
5727   // draw masked border over door area
5728   DrawMaskedBorder(REDRAW_DOOR_1);
5729   DrawMaskedBorder(REDRAW_DOOR_2);
5730
5731   ClearAutoRepeatKeyEvents();
5732
5733   return (door1 | door2);
5734 }
5735
5736 static boolean useSpecialEditorDoor(void)
5737 {
5738   int graphic = IMG_GLOBAL_BORDER_EDITOR;
5739   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5740
5741   // do not draw special editor door if editor border defined or redefined
5742   if (graphic_info[graphic].bitmap != NULL || redefined)
5743     return FALSE;
5744
5745   // do not draw special editor door if global border defined to be empty
5746   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5747     return FALSE;
5748
5749   // do not draw special editor door if viewport definitions do not match
5750   if (EX != VX ||
5751       EY >= VY ||
5752       EXSIZE != VXSIZE ||
5753       EY + EYSIZE != VY + VYSIZE)
5754     return FALSE;
5755
5756   return TRUE;
5757 }
5758
5759 void DrawSpecialEditorDoor(void)
5760 {
5761   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5762   int top_border_width = gfx1->width;
5763   int top_border_height = gfx1->height;
5764   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5765   int ex = EX - outer_border;
5766   int ey = EY - outer_border;
5767   int vy = VY - outer_border;
5768   int exsize = EXSIZE + 2 * outer_border;
5769
5770   if (!useSpecialEditorDoor())
5771     return;
5772
5773   // draw bigger level editor toolbox window
5774   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5775              top_border_width, top_border_height, ex, ey - top_border_height);
5776   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5777              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5778
5779   redraw_mask |= REDRAW_ALL;
5780 }
5781
5782 void UndrawSpecialEditorDoor(void)
5783 {
5784   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5785   int top_border_width = gfx1->width;
5786   int top_border_height = gfx1->height;
5787   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5788   int ex = EX - outer_border;
5789   int ey = EY - outer_border;
5790   int ey_top = ey - top_border_height;
5791   int exsize = EXSIZE + 2 * outer_border;
5792   int eysize = EYSIZE + 2 * outer_border;
5793
5794   if (!useSpecialEditorDoor())
5795     return;
5796
5797   // draw normal tape recorder window
5798   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5799   {
5800     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5801                ex, ey_top, top_border_width, top_border_height,
5802                ex, ey_top);
5803     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5804                ex, ey, exsize, eysize, ex, ey);
5805   }
5806   else
5807   {
5808     // if screen background is set to "[NONE]", clear editor toolbox window
5809     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5810     ClearRectangle(drawto, ex, ey, exsize, eysize);
5811   }
5812
5813   redraw_mask |= REDRAW_ALL;
5814 }
5815
5816
5817 // ---------- new tool button stuff -------------------------------------------
5818
5819 static struct
5820 {
5821   int graphic;
5822   struct TextPosInfo *pos;
5823   int gadget_id;
5824   boolean is_touch_button;
5825   char *infotext;
5826 } toolbutton_info[NUM_TOOL_BUTTONS] =
5827 {
5828   {
5829     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
5830     TOOL_CTRL_ID_YES, FALSE,            "yes"
5831   },
5832   {
5833     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5834     TOOL_CTRL_ID_NO, FALSE,             "no"
5835   },
5836   {
5837     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5838     TOOL_CTRL_ID_CONFIRM, FALSE,        "confirm"
5839   },
5840   {
5841     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5842     TOOL_CTRL_ID_PLAYER_1, FALSE,       "player 1"
5843   },
5844   {
5845     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5846     TOOL_CTRL_ID_PLAYER_2, FALSE,       "player 2"
5847   },
5848   {
5849     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5850     TOOL_CTRL_ID_PLAYER_3, FALSE,       "player 3"
5851   },
5852   {
5853     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5854     TOOL_CTRL_ID_PLAYER_4, FALSE,       "player 4"
5855   },
5856   {
5857     IMG_GFX_REQUEST_BUTTON_TOUCH_YES,   &request.button.touch_yes,
5858     TOOL_CTRL_ID_TOUCH_YES, TRUE,       "yes"
5859   },
5860   {
5861     IMG_GFX_REQUEST_BUTTON_TOUCH_NO,    &request.button.touch_no,
5862     TOOL_CTRL_ID_TOUCH_NO, TRUE,        "no"
5863   },
5864   {
5865     IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5866     TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE,   "confirm"
5867   }
5868 };
5869
5870 void CreateToolButtons(void)
5871 {
5872   int i;
5873
5874   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5875   {
5876     int graphic = toolbutton_info[i].graphic;
5877     struct GraphicInfo *gfx = &graphic_info[graphic];
5878     struct TextPosInfo *pos = toolbutton_info[i].pos;
5879     struct GadgetInfo *gi;
5880     Bitmap *deco_bitmap = None;
5881     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5882     unsigned int event_mask = GD_EVENT_RELEASED;
5883     boolean is_touch_button = toolbutton_info[i].is_touch_button;
5884     int base_x = (is_touch_button ? 0 : DX);
5885     int base_y = (is_touch_button ? 0 : DY);
5886     int gd_x = gfx->src_x;
5887     int gd_y = gfx->src_y;
5888     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5889     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5890     int x = pos->x;
5891     int y = pos->y;
5892     int id = i;
5893
5894     // do not use touch buttons if overlay touch buttons are disabled
5895     if (is_touch_button && !setup.touch.overlay_buttons)
5896       continue;
5897
5898     if (global.use_envelope_request && !is_touch_button)
5899     {
5900       setRequestPosition(&base_x, &base_y, TRUE);
5901
5902       // check if request buttons are outside of envelope and fix, if needed
5903       if (x < 0 || x + gfx->width  > request.width ||
5904           y < 0 || y + gfx->height > request.height)
5905       {
5906         if (id == TOOL_CTRL_ID_YES)
5907         {
5908           x = 0;
5909           y = request.height - 2 * request.border_size - gfx->height;
5910         }
5911         else if (id == TOOL_CTRL_ID_NO)
5912         {
5913           x = request.width  - 2 * request.border_size - gfx->width;
5914           y = request.height - 2 * request.border_size - gfx->height;
5915         }
5916         else if (id == TOOL_CTRL_ID_CONFIRM)
5917         {
5918           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5919           y = request.height - 2 * request.border_size - gfx->height;
5920         }
5921         else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5922         {
5923           int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5924
5925           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5926           y = request.height - 2 * request.border_size - gfx->height * 2;
5927
5928           x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5929           y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5930         }
5931       }
5932     }
5933
5934     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5935         pos->draw_player)
5936     {
5937       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5938
5939       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5940                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5941       deco_xpos = (gfx->width  - pos->size) / 2;
5942       deco_ypos = (gfx->height - pos->size) / 2;
5943     }
5944
5945     gi = CreateGadget(GDI_CUSTOM_ID, id,
5946                       GDI_IMAGE_ID, graphic,
5947                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5948                       GDI_X, base_x + x,
5949                       GDI_Y, base_y + y,
5950                       GDI_WIDTH, gfx->width,
5951                       GDI_HEIGHT, gfx->height,
5952                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5953                       GDI_STATE, GD_BUTTON_UNPRESSED,
5954                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5955                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5956                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5957                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5958                       GDI_DECORATION_SIZE, pos->size, pos->size,
5959                       GDI_DECORATION_SHIFTING, 1, 1,
5960                       GDI_DIRECT_DRAW, FALSE,
5961                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5962                       GDI_EVENT_MASK, event_mask,
5963                       GDI_CALLBACK_ACTION, HandleToolButtons,
5964                       GDI_END);
5965
5966     if (gi == NULL)
5967       Fail("cannot create gadget");
5968
5969     tool_gadget[id] = gi;
5970   }
5971 }
5972
5973 void FreeToolButtons(void)
5974 {
5975   int i;
5976
5977   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5978     FreeGadget(tool_gadget[i]);
5979 }
5980
5981 static void MapToolButtons(unsigned int req_state)
5982 {
5983   if (req_state & REQ_ASK)
5984   {
5985     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5986     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5987     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5988     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5989   }
5990   else if (req_state & REQ_CONFIRM)
5991   {
5992     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5993     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5994   }
5995   else if (req_state & REQ_PLAYER)
5996   {
5997     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5998     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5999     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
6000     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
6001   }
6002 }
6003
6004 static void UnmapToolButtons(void)
6005 {
6006   int i;
6007
6008   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
6009     UnmapGadget(tool_gadget[i]);
6010 }
6011
6012 static void HandleToolButtons(struct GadgetInfo *gi)
6013 {
6014   request_gadget_id = gi->custom_id;
6015 }
6016
6017 static int getEngineElement_Ext(int element, int game_engine_type, boolean is_drawing_element)
6018 {
6019   int el_empty;
6020   int el_player;
6021   int el_sand;
6022   int el_wall;
6023   int el_steelwall;
6024   int el_exit_closed;
6025
6026   if (game_engine_type == -1)
6027     game_engine_type = level.game_engine_type;
6028
6029   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
6030   {
6031     el_empty            = EL_EMPTY;
6032     el_player           = EL_BDX_PLAYER;
6033     el_sand             = EL_BDX_SAND_1;
6034     el_wall             = EL_BDX_WALL;
6035     el_steelwall        = EL_BDX_STEELWALL;
6036     el_exit_closed      = EL_BDX_EXIT_CLOSED;
6037   }
6038   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6039   {
6040     el_empty            = EL_EMPTY;
6041     el_player           = EL_PLAYER_1;
6042     el_sand             = EL_SAND;
6043     el_wall             = EL_WALL;
6044     el_steelwall        = EL_STEELWALL;
6045     el_exit_closed      = EL_EM_EXIT_CLOSED;
6046   }
6047   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6048   {
6049     el_empty            = EL_EMPTY;
6050     el_player           = EL_SP_MURPHY;
6051     el_sand             = EL_SP_BASE;
6052     el_wall             = EL_SP_CHIP_SINGLE;
6053     el_steelwall        = EL_SP_HARDWARE_GRAY;
6054     el_exit_closed      = EL_SP_EXIT_CLOSED;
6055   }
6056   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
6057   {
6058     el_empty            = EL_EMPTY;
6059     el_player           = EL_MM_MCDUFFIN_DOWN;
6060     el_sand             = EL_EMPTY;
6061     el_wall             = EL_MM_WOODEN_WALL;
6062     el_steelwall        = EL_MM_STEEL_WALL;
6063     el_exit_closed      = EL_MM_EXIT_CLOSED;
6064
6065     if (is_drawing_element)
6066     {
6067       el_wall           = EL_MM_MIRROR_START;
6068       el_sand           = EL_MM_WOODEN_WALL;
6069     }
6070   }
6071   else
6072   {
6073     el_empty            = EL_EMPTY;
6074     el_player           = EL_PLAYER_1;
6075     el_sand             = EL_SAND;
6076     el_wall             = EL_WALL;
6077     el_steelwall        = EL_STEELWALL;
6078     el_exit_closed      = EL_EXIT_CLOSED;
6079   }
6080
6081   return (element == EL_EMPTY           ? el_empty :
6082           element == EL_PLAYER_1        ? el_player :
6083           element == EL_SAND            ? el_sand :
6084           element == EL_WALL            ? el_wall :
6085           element == EL_STEELWALL       ? el_steelwall :
6086           element == EL_EXIT_CLOSED     ? el_exit_closed : EL_EMPTY);
6087 }
6088
6089 int getEngineElement(int element)
6090 {
6091   return getEngineElement_Ext(element, -1, FALSE);
6092 }
6093
6094 int getDrawingElement(int element)
6095 {
6096   return getEngineElement_Ext(element, -1, TRUE);
6097 }
6098
6099 static struct Mapping_BD_to_RND_object
6100 {
6101   int element_bd;
6102   boolean is_rnd_to_bd_mapping;         // unique mapping BD <-> RND
6103
6104   int element_rnd;
6105   int action;
6106   int direction;
6107 }
6108 bd_object_mapping_list[] =
6109 {
6110   // additional RND style elements mapped to BD style elements (must be listed first)
6111
6112   {
6113     O_DIRT,                                     TRUE,
6114     EL_SAND,                                    -1, -1
6115   },
6116   {
6117     O_STONE,                                    TRUE,
6118     EL_BD_ROCK,                                 -1, -1
6119   },
6120   {
6121     O_BRICK,                                    TRUE,
6122     EL_BD_WALL,                                 -1, -1
6123   },
6124   {
6125     O_STEEL,                                    TRUE,
6126     EL_STEELWALL,                               -1, -1
6127   },
6128   {
6129     O_DIAMOND,                                  TRUE,
6130     EL_BD_DIAMOND,                              -1, -1
6131   },
6132   {
6133     O_INBOX,                                    TRUE,
6134     EL_PLAYER_1,                                -1, -1
6135   },
6136   {
6137     O_INBOX,                                    TRUE,
6138     EL_PLAYER_2,                                -1, -1
6139   },
6140   {
6141     O_INBOX,                                    TRUE,
6142     EL_PLAYER_3,                                -1, -1
6143   },
6144   {
6145     O_INBOX,                                    TRUE,
6146     EL_PLAYER_4,                                -1, -1
6147   },
6148   {
6149     O_PRE_OUTBOX,                               TRUE,
6150     EL_EXIT_CLOSED,                             -1, -1
6151   },
6152
6153   // BD style elements with their corresponding RND style elements
6154
6155   {
6156     O_SPACE,                                    TRUE,
6157     EL_EMPTY,                                   -1, -1
6158   },
6159   {
6160     O_DIRT,                                     TRUE,
6161     EL_BDX_SAND_1,                              -1, -1
6162   },
6163   {
6164     O_DIRT_SLOPED_UP_RIGHT,                     TRUE,
6165     EL_BDX_SAND_SLOPED_UP_RIGHT,                -1, -1
6166   },
6167   {
6168     O_DIRT_SLOPED_UP_LEFT,                      TRUE,
6169     EL_BDX_SAND_SLOPED_UP_LEFT,                 -1, -1
6170   },
6171   {
6172     O_DIRT_SLOPED_DOWN_LEFT,                    TRUE,
6173     EL_BDX_SAND_SLOPED_DOWN_LEFT,               -1, -1
6174   },
6175   {
6176     O_DIRT_SLOPED_DOWN_RIGHT,                   TRUE,
6177     EL_BDX_SAND_SLOPED_DOWN_RIGHT,              -1, -1
6178   },
6179   {
6180     O_DIRT_BALL,                                TRUE,
6181     EL_BDX_SAND_BALL,                           -1, -1
6182   },
6183   {
6184     O_DIRT_BALL_F,                              TRUE,
6185     EL_BDX_SAND_BALL_FALLING,                   -1, -1
6186   },
6187   {
6188     O_DIRT_BALL_F,                              FALSE,
6189     EL_BDX_SAND_BALL,                           ACTION_FALLING, -1
6190   },
6191   {
6192     O_DIRT_LOOSE,                               TRUE,
6193     EL_BDX_SAND_LOOSE,                          -1, -1
6194   },
6195   {
6196     O_DIRT_LOOSE_F,                             TRUE,
6197     EL_BDX_SAND_LOOSE_FALLING,                  -1, -1
6198   },
6199   {
6200     O_DIRT_LOOSE_F,                             FALSE,
6201     EL_BDX_SAND_LOOSE,                          ACTION_FALLING, -1
6202   },
6203   {
6204     O_DIRT2,                                    TRUE,
6205     EL_BDX_SAND_2,                              -1, -1
6206   },
6207   {
6208     O_BRICK,                                    TRUE,
6209     EL_BDX_WALL,                                -1, -1
6210   },
6211   {
6212     O_BRICK_SLOPED_UP_RIGHT,                    TRUE,
6213     EL_BDX_WALL_SLOPED_UP_RIGHT,                -1, -1
6214   },
6215   {
6216     O_BRICK_SLOPED_UP_LEFT,                     TRUE,
6217     EL_BDX_WALL_SLOPED_UP_LEFT,                 -1, -1
6218   },
6219   {
6220     O_BRICK_SLOPED_DOWN_LEFT,                   TRUE,
6221     EL_BDX_WALL_SLOPED_DOWN_LEFT,               -1, -1
6222   },
6223   {
6224     O_BRICK_SLOPED_DOWN_RIGHT,                  TRUE,
6225     EL_BDX_WALL_SLOPED_DOWN_RIGHT,              -1, -1
6226   },
6227   {
6228     O_BRICK_NON_SLOPED,                         TRUE,
6229     EL_BDX_WALL_NON_SLOPED,                     -1, -1
6230   },
6231   {
6232     O_MAGIC_WALL,                               TRUE,
6233     EL_BDX_MAGIC_WALL,                          ACTION_ACTIVE, -1
6234   },
6235   {
6236     O_PRE_OUTBOX,                               TRUE,
6237     EL_BDX_EXIT_CLOSED,                         -1, -1
6238   },
6239   {
6240     O_OUTBOX,                                   TRUE,
6241     EL_BDX_EXIT_OPEN,                           -1, -1
6242   },
6243   {
6244     O_PRE_INVIS_OUTBOX,                         TRUE,
6245     EL_BDX_INVISIBLE_EXIT_CLOSED,               -1, -1
6246   },
6247   {
6248     O_INVIS_OUTBOX,                             TRUE,
6249     EL_BDX_INVISIBLE_EXIT_OPEN,                 -1, -1
6250   },
6251   {
6252     O_STEEL,                                    TRUE,
6253     EL_BDX_STEELWALL,                           -1, -1
6254   },
6255   {
6256     O_STEEL_SLOPED_UP_RIGHT,                    TRUE,
6257     EL_BDX_STEELWALL_SLOPED_UP_RIGHT,           -1, -1
6258   },
6259   {
6260     O_STEEL_SLOPED_UP_LEFT,                     TRUE,
6261     EL_BDX_STEELWALL_SLOPED_UP_LEFT,            -1, -1
6262   },
6263   {
6264     O_STEEL_SLOPED_DOWN_LEFT,                   TRUE,
6265     EL_BDX_STEELWALL_SLOPED_DOWN_LEFT,          -1, -1
6266   },
6267   {
6268     O_STEEL_SLOPED_DOWN_RIGHT,                  TRUE,
6269     EL_BDX_STEELWALL_SLOPED_DOWN_RIGHT,         -1, -1
6270   },
6271   {
6272     O_STEEL_EXPLODABLE,                         TRUE,
6273     EL_BDX_STEELWALL_EXPLODABLE,                -1, -1
6274   },
6275   {
6276     O_STEEL_EATABLE,                            TRUE,
6277     EL_BDX_STEELWALL_DIGGABLE,                  -1, -1
6278   },
6279   {
6280     O_BRICK_EATABLE,                            TRUE,
6281     EL_BDX_WALL_DIGGABLE,                       -1, -1
6282   },
6283   {
6284     O_STONE,                                    TRUE,
6285     EL_BDX_ROCK,                                -1, -1
6286   },
6287   {
6288     O_STONE_F,                                  TRUE,
6289     EL_BDX_ROCK_FALLING,                        -1, -1
6290   },
6291   {
6292     O_STONE_F,                                  FALSE,
6293     EL_BDX_ROCK,                                ACTION_FALLING, -1
6294   },
6295   {
6296     O_FLYING_STONE,                             TRUE,
6297     EL_BDX_FLYING_ROCK,                         -1, -1
6298   },
6299   {
6300     O_FLYING_STONE_F,                           TRUE,
6301     EL_BDX_FLYING_ROCK_FLYING,                  -1, -1
6302   },
6303   {
6304     O_FLYING_STONE_F,                           FALSE,
6305     EL_BDX_FLYING_ROCK,                         ACTION_FLYING, -1
6306   },
6307   {
6308     O_MEGA_STONE,                               TRUE,
6309     EL_BDX_MEGA_ROCK,                           -1, -1
6310   },
6311   {
6312     O_MEGA_STONE_F,                             TRUE,
6313     EL_BDX_MEGA_ROCK_FALLING,                   -1, -1
6314   },
6315   {
6316     O_MEGA_STONE_F,                             FALSE,
6317     EL_BDX_MEGA_ROCK,                           ACTION_FALLING, -1
6318   },
6319   {
6320     O_DIAMOND,                                  TRUE,
6321     EL_BDX_DIAMOND,                             -1, -1
6322   },
6323   {
6324     O_DIAMOND_F,                                TRUE,
6325     EL_BDX_DIAMOND_FALLING,                     -1, -1
6326   },
6327   {
6328     O_DIAMOND_F,                                FALSE,
6329     EL_BDX_DIAMOND,                             ACTION_FALLING, -1
6330   },
6331   {
6332     O_FLYING_DIAMOND,                           TRUE,
6333     EL_BDX_FLYING_DIAMOND,                      -1, -1
6334   },
6335   {
6336     O_FLYING_DIAMOND_F,                         TRUE,
6337     EL_BDX_FLYING_DIAMOND_FLYING,               -1, -1
6338   },
6339   {
6340     O_FLYING_DIAMOND_F,                         FALSE,
6341     EL_BDX_FLYING_DIAMOND,                      ACTION_FLYING, -1
6342   },
6343   {
6344     O_NUT,                                      TRUE,
6345     EL_BDX_NUT,                                 -1, -1
6346   },
6347   {
6348     O_NUT_F,                                    TRUE,
6349     EL_BDX_NUT_FALLING,                         -1, -1
6350   },
6351   {
6352     O_NUT_F,                                    FALSE,
6353     EL_BDX_NUT,                                 ACTION_FALLING, -1
6354   },
6355   {
6356     O_BLADDER_SPENDER,                          TRUE,
6357     EL_BDX_TRAPPED_BUBBLE,                      -1, -1
6358   },
6359   {
6360     O_INBOX,                                    TRUE,
6361     EL_BDX_INBOX,                               -1, -1
6362   },
6363   {
6364     O_H_EXPANDING_WALL,                         TRUE,
6365     EL_BDX_EXPANDABLE_WALL_HORIZONTAL,          -1, -1
6366   },
6367   {
6368     O_V_EXPANDING_WALL,                         TRUE,
6369     EL_BDX_EXPANDABLE_WALL_VERTICAL,            -1, -1
6370   },
6371   {
6372     O_EXPANDING_WALL,                           TRUE,
6373     EL_BDX_EXPANDABLE_WALL_ANY,                 -1, -1
6374   },
6375   {
6376     O_H_EXPANDING_STEEL_WALL,                   TRUE,
6377     EL_BDX_EXPANDABLE_STEELWALL_HORIZONTAL,     -1, -1
6378   },
6379   {
6380     O_V_EXPANDING_STEEL_WALL,                   TRUE,
6381     EL_BDX_EXPANDABLE_STEELWALL_VERTICAL,       -1, -1
6382   },
6383   {
6384     O_EXPANDING_STEEL_WALL,                     TRUE,
6385     EL_BDX_EXPANDABLE_STEELWALL_ANY,            -1, -1
6386   },
6387   {
6388     O_EXPANDING_WALL_SWITCH,                    TRUE,
6389     EL_BDX_EXPANDABLE_WALL_SWITCH,              -1, -1
6390   },
6391   {
6392     O_CREATURE_SWITCH,                          TRUE,
6393     EL_BDX_CREATURE_SWITCH,                     -1, -1
6394   },
6395   {
6396     O_BITER_SWITCH,                             TRUE,
6397     EL_BDX_BITER_SWITCH_1,                      -1, -1
6398   },
6399   {
6400     O_REPLICATOR_SWITCH,                        TRUE,
6401     EL_BDX_REPLICATOR_SWITCH,                   -1, -1
6402   },
6403   {
6404     O_CONVEYOR_SWITCH,                          TRUE,
6405     EL_BDX_CONVEYOR_SWITCH,                     -1, -1
6406   },
6407   {
6408     O_CONVEYOR_DIR_SWITCH,                      TRUE,
6409     EL_BDX_CONVEYOR_DIR_SWITCH,                 -1, -1
6410   },
6411   {
6412     O_ACID,                                     TRUE,
6413     EL_BDX_ACID,                                -1, -1
6414   },
6415   {
6416     O_FALLING_WALL,                             TRUE,
6417     EL_BDX_FALLING_WALL,                        -1, -1
6418   },
6419   {
6420     O_FALLING_WALL_F,                           TRUE,
6421     EL_BDX_FALLING_WALL_FALLING,                -1, -1
6422   },
6423   {
6424     O_FALLING_WALL_F,                           FALSE,
6425     EL_BDX_FALLING_WALL,                        ACTION_FALLING, -1
6426   },
6427   {
6428     O_BOX,                                      TRUE,
6429     EL_BDX_BOX,                                 -1, -1
6430   },
6431   {
6432     O_TIME_PENALTY,                             TRUE,
6433     EL_BDX_TIME_PENALTY,                        -1, -1
6434   },
6435   {
6436     O_GRAVESTONE,                               TRUE,
6437     EL_BDX_GRAVESTONE,                          -1, -1
6438   },
6439   {
6440     O_STONE_GLUED,                              TRUE,
6441     EL_BDX_ROCK_GLUED,                          -1, -1
6442   },
6443   {
6444     O_DIAMOND_GLUED,                            TRUE,
6445     EL_BDX_DIAMOND_GLUED,                       -1, -1
6446   },
6447   {
6448     O_DIAMOND_KEY,                              TRUE,
6449     EL_BDX_DIAMOND_KEY,                         -1, -1
6450   },
6451   {
6452     O_TRAPPED_DIAMOND,                          TRUE,
6453     EL_BDX_TRAPPED_DIAMOND,                     -1, -1
6454   },
6455   {
6456     O_CLOCK,                                    TRUE,
6457     EL_BDX_CLOCK,                               -1, -1
6458   },
6459   {
6460     O_DIRT_GLUED,                               TRUE,
6461     EL_BDX_SAND_GLUED,                          -1, -1
6462   },
6463   {
6464     O_KEY_1,                                    TRUE,
6465     EL_BDX_KEY_1,                               -1, -1
6466   },
6467   {
6468     O_KEY_2,                                    TRUE,
6469     EL_BDX_KEY_2,                               -1, -1
6470   },
6471   {
6472     O_KEY_3,                                    TRUE,
6473     EL_BDX_KEY_3,                               -1, -1
6474   },
6475   {
6476     O_DOOR_1,                                   TRUE,
6477     EL_BDX_GATE_1,                              -1, -1
6478   },
6479   {
6480     O_DOOR_2,                                   TRUE,
6481     EL_BDX_GATE_2,                              -1, -1
6482   },
6483   {
6484     O_DOOR_3,                                   TRUE,
6485     EL_BDX_GATE_3,                              -1, -1
6486   },
6487   {
6488     O_POT,                                      TRUE,
6489     EL_BDX_POT,                                 -1, -1
6490   },
6491   {
6492     O_GRAVITY_SWITCH,                           TRUE,
6493     EL_BDX_GRAVITY_SWITCH,                      -1, -1
6494   },
6495   {
6496     O_PNEUMATIC_HAMMER,                         TRUE,
6497     EL_BDX_PNEUMATIC_HAMMER,                    -1, -1
6498   },
6499   {
6500     O_TELEPORTER,                               TRUE,
6501     EL_BDX_TELEPORTER,                          -1, -1
6502   },
6503   {
6504     O_SKELETON,                                 TRUE,
6505     EL_BDX_SKELETON,                            -1, -1
6506   },
6507   {
6508     O_WATER,                                    TRUE,
6509     EL_BDX_WATER,                               -1, -1
6510   },
6511   {
6512     O_WATER_1,                                  TRUE,
6513     EL_BDX_WATER_1,                             -1, -1
6514   },
6515   {
6516     O_WATER_1,                                  FALSE,
6517     EL_BDX_WATER,                               -1, -1
6518   },
6519   {
6520     O_WATER_2,                                  TRUE,
6521     EL_BDX_WATER_2,                             -1, -1
6522   },
6523   {
6524     O_WATER_2,                                  FALSE,
6525     EL_BDX_WATER,                               -1, -1
6526   },
6527   {
6528     O_WATER_3,                                  TRUE,
6529     EL_BDX_WATER_3,                             -1, -1
6530   },
6531   {
6532     O_WATER_3,                                  FALSE,
6533     EL_BDX_WATER,                               -1, -1
6534   },
6535   {
6536     O_WATER_4,                                  TRUE,
6537     EL_BDX_WATER_4,                             -1, -1
6538   },
6539   {
6540     O_WATER_4,                                  FALSE,
6541     EL_BDX_WATER,                               -1, -1
6542   },
6543   {
6544     O_WATER_5,                                  TRUE,
6545     EL_BDX_WATER_5,                             -1, -1
6546   },
6547   {
6548     O_WATER_5,                                  FALSE,
6549     EL_BDX_WATER,                               -1, -1
6550   },
6551   {
6552     O_WATER_6,                                  TRUE,
6553     EL_BDX_WATER_6,                             -1, -1
6554   },
6555   {
6556     O_WATER_6,                                  FALSE,
6557     EL_BDX_WATER,                               -1, -1
6558   },
6559   {
6560     O_WATER_7,                                  TRUE,
6561     EL_BDX_WATER_7,                             -1, -1
6562   },
6563   {
6564     O_WATER_7,                                  FALSE,
6565     EL_BDX_WATER,                               -1, -1
6566   },
6567   {
6568     O_WATER_8,                                  TRUE,
6569     EL_BDX_WATER_8,                             -1, -1
6570   },
6571   {
6572     O_WATER_8,                                  FALSE,
6573     EL_BDX_WATER,                               -1, -1
6574   },
6575   {
6576     O_WATER_9,                                  TRUE,
6577     EL_BDX_WATER_9,                             -1, -1
6578   },
6579   {
6580     O_WATER_9,                                  FALSE,
6581     EL_BDX_WATER,                               -1, -1
6582   },
6583   {
6584     O_WATER_10,                                 TRUE,
6585     EL_BDX_WATER_10,                            -1, -1
6586   },
6587   {
6588     O_WATER_10,                                 FALSE,
6589     EL_BDX_WATER,                               -1, -1
6590   },
6591   {
6592     O_WATER_11,                                 TRUE,
6593     EL_BDX_WATER_11,                            -1, -1
6594   },
6595   {
6596     O_WATER_11,                                 FALSE,
6597     EL_BDX_WATER,                               -1, -1
6598   },
6599   {
6600     O_WATER_12,                                 TRUE,
6601     EL_BDX_WATER_12,                            -1, -1
6602   },
6603   {
6604     O_WATER_12,                                 FALSE,
6605     EL_BDX_WATER,                               -1, -1
6606   },
6607   {
6608     O_WATER_13,                                 TRUE,
6609     EL_BDX_WATER_13,                            -1, -1
6610   },
6611   {
6612     O_WATER_13,                                 FALSE,
6613     EL_BDX_WATER,                               -1, -1
6614   },
6615   {
6616     O_WATER_14,                                 TRUE,
6617     EL_BDX_WATER_14,                            -1, -1
6618   },
6619   {
6620     O_WATER_14,                                 FALSE,
6621     EL_BDX_WATER,                               -1, -1
6622   },
6623   {
6624     O_WATER_15,                                 TRUE,
6625     EL_BDX_WATER_15,                            -1, -1
6626   },
6627   {
6628     O_WATER_15,                                 FALSE,
6629     EL_BDX_WATER,                               -1, -1
6630   },
6631   {
6632     O_WATER_16,                                 TRUE,
6633     EL_BDX_WATER_16,                            -1, -1
6634   },
6635   {
6636     O_WATER_16,                                 FALSE,
6637     EL_BDX_WATER,                               -1, -1
6638   },
6639   {
6640     O_COW_1,                                    TRUE,
6641     EL_BDX_COW_LEFT,                            -1, -1
6642   },
6643   {
6644     O_COW_2,                                    TRUE,
6645     EL_BDX_COW_UP,                              -1, -1
6646   },
6647   {
6648     O_COW_3,                                    TRUE,
6649     EL_BDX_COW_RIGHT,                           -1, -1
6650   },
6651   {
6652     O_COW_4,                                    TRUE,
6653     EL_BDX_COW_DOWN,                            -1, -1
6654   },
6655   {
6656     O_COW_ENCLOSED_1,                           TRUE,
6657     EL_BDX_COW_ENCLOSED_1,                      -1, -1
6658   },
6659   {
6660     O_COW_ENCLOSED_1,                           FALSE,
6661     EL_BDX_COW_DOWN,                            -1, -1
6662   },
6663   {
6664     O_COW_ENCLOSED_2,                           TRUE,
6665     EL_BDX_COW_ENCLOSED_2,                      -1, -1
6666   },
6667   {
6668     O_COW_ENCLOSED_2,                           FALSE,
6669     EL_BDX_COW_DOWN,                            -1, -1
6670   },
6671   {
6672     O_COW_ENCLOSED_3,                           TRUE,
6673     EL_BDX_COW_ENCLOSED_3,                      -1, -1
6674   },
6675   {
6676     O_COW_ENCLOSED_3,                           FALSE,
6677     EL_BDX_COW_DOWN,                            -1, -1
6678   },
6679   {
6680     O_COW_ENCLOSED_4,                           TRUE,
6681     EL_BDX_COW_ENCLOSED_4,                      -1, -1
6682   },
6683   {
6684     O_COW_ENCLOSED_4,                           FALSE,
6685     EL_BDX_COW_DOWN,                            -1, -1
6686   },
6687   {
6688     O_COW_ENCLOSED_5,                           TRUE,
6689     EL_BDX_COW_ENCLOSED_5,                      -1, -1
6690   },
6691   {
6692     O_COW_ENCLOSED_5,                           FALSE,
6693     EL_BDX_COW_DOWN,                            -1, -1
6694   },
6695   {
6696     O_COW_ENCLOSED_6,                           TRUE,
6697     EL_BDX_COW_ENCLOSED_6,                      -1, -1
6698   },
6699   {
6700     O_COW_ENCLOSED_6,                           FALSE,
6701     EL_BDX_COW_DOWN,                            -1, -1
6702   },
6703   {
6704     O_COW_ENCLOSED_7,                           TRUE,
6705     EL_BDX_COW_ENCLOSED_7,                      -1, -1
6706   },
6707   {
6708     O_COW_ENCLOSED_7,                           FALSE,
6709     EL_BDX_COW_DOWN,                            -1, -1
6710   },
6711   {
6712     O_WALLED_DIAMOND,                           TRUE,
6713     EL_BDX_WALL_DIAMOND,                        -1, -1
6714   },
6715   {
6716     O_WALLED_KEY_1,                             TRUE,
6717     EL_BDX_WALL_KEY_1,                          -1, -1
6718   },
6719   {
6720     O_WALLED_KEY_2,                             TRUE,
6721     EL_BDX_WALL_KEY_2,                          -1, -1
6722   },
6723   {
6724     O_WALLED_KEY_3,                             TRUE,
6725     EL_BDX_WALL_KEY_3,                          -1, -1
6726   },
6727   {
6728     O_AMOEBA,                                   TRUE,
6729     EL_BDX_AMOEBA_1,                            -1, -1
6730   },
6731   {
6732     O_AMOEBA_2,                                 TRUE,
6733     EL_BDX_AMOEBA_2,                            -1, -1
6734   },
6735   {
6736     O_REPLICATOR,                               TRUE,
6737     EL_BDX_REPLICATOR,                          -1, -1
6738   },
6739   {
6740     O_CONVEYOR_LEFT,                            TRUE,
6741     EL_BDX_CONVEYOR_LEFT,                       -1, -1
6742   },
6743   {
6744     O_CONVEYOR_RIGHT,                           TRUE,
6745     EL_BDX_CONVEYOR_RIGHT,                      -1, -1
6746   },
6747   {
6748     O_LAVA,                                     TRUE,
6749     EL_BDX_LAVA,                                -1, -1
6750   },
6751   {
6752     O_SWEET,                                    TRUE,
6753     EL_BDX_SWEET,                               -1, -1
6754   },
6755   {
6756     O_VOODOO,                                   TRUE,
6757     EL_BDX_VOODOO_DOLL,                         -1, -1
6758   },
6759   {
6760     O_SLIME,                                    TRUE,
6761     EL_BDX_SLIME,                               -1, -1
6762   },
6763   {
6764     O_BLADDER,                                  TRUE,
6765     EL_BDX_BUBBLE,                              -1, -1
6766   },
6767   {
6768     O_BLADDER_1,                                TRUE,
6769     EL_BDX_BUBBLE_1,                            -1, -1
6770   },
6771   {
6772     O_BLADDER_1,                                FALSE,
6773     EL_BDX_BUBBLE,                              -1, -1
6774   },
6775   {
6776     O_BLADDER_2,                                TRUE,
6777     EL_BDX_BUBBLE_2,                            -1, -1
6778   },
6779   {
6780     O_BLADDER_2,                                FALSE,
6781     EL_BDX_BUBBLE,                              -1, -1
6782   },
6783   {
6784     O_BLADDER_3,                                TRUE,
6785     EL_BDX_BUBBLE_3,                            -1, -1
6786   },
6787   {
6788     O_BLADDER_3,                                FALSE,
6789     EL_BDX_BUBBLE,                              -1, -1
6790   },
6791   {
6792     O_BLADDER_4,                                TRUE,
6793     EL_BDX_BUBBLE_4,                            -1, -1
6794   },
6795   {
6796     O_BLADDER_4,                                FALSE,
6797     EL_BDX_BUBBLE,                              -1, -1
6798   },
6799   {
6800     O_BLADDER_5,                                TRUE,
6801     EL_BDX_BUBBLE_5,                            -1, -1
6802   },
6803   {
6804     O_BLADDER_5,                                FALSE,
6805     EL_BDX_BUBBLE,                              -1, -1
6806   },
6807   {
6808     O_BLADDER_6,                                TRUE,
6809     EL_BDX_BUBBLE_6,                            -1, -1
6810   },
6811   {
6812     O_BLADDER_6,                                FALSE,
6813     EL_BDX_BUBBLE,                              -1, -1
6814   },
6815   {
6816     O_BLADDER_7,                                TRUE,
6817     EL_BDX_BUBBLE_7,                            -1, -1
6818   },
6819   {
6820     O_BLADDER_7,                                FALSE,
6821     EL_BDX_BUBBLE,                              -1, -1
6822   },
6823   {
6824     O_BLADDER_8,                                TRUE,
6825     EL_BDX_BUBBLE_8,                            -1, -1
6826   },
6827   {
6828     O_BLADDER_8,                                FALSE,
6829     EL_BDX_BUBBLE,                              -1, -1
6830   },
6831   {
6832     O_WAITING_STONE,                            TRUE,
6833     EL_BDX_WAITING_ROCK,                        -1, -1
6834   },
6835   {
6836     O_CHASING_STONE,                            TRUE,
6837     EL_BDX_CHASING_ROCK,                        -1, -1
6838   },
6839   {
6840     O_GHOST,                                    TRUE,
6841     EL_BDX_GHOST,                               -1, -1
6842   },
6843   {
6844     O_FIREFLY_1,                                TRUE,
6845     EL_BDX_FIREFLY_1_LEFT,                      -1, -1
6846   },
6847   {
6848     O_FIREFLY_2,                                TRUE,
6849     EL_BDX_FIREFLY_1_UP,                        -1, -1
6850   },
6851   {
6852     O_FIREFLY_3,                                TRUE,
6853     EL_BDX_FIREFLY_1_RIGHT,                     -1, -1
6854   },
6855   {
6856     O_FIREFLY_4,                                TRUE,
6857     EL_BDX_FIREFLY_1_DOWN,                      -1, -1
6858   },
6859   {
6860     O_ALT_FIREFLY_1,                            TRUE,
6861     EL_BDX_FIREFLY_2_LEFT,                      -1, -1
6862   },
6863   {
6864     O_ALT_FIREFLY_2,                            TRUE,
6865     EL_BDX_FIREFLY_2_UP,                        -1, -1
6866   },
6867   {
6868     O_ALT_FIREFLY_3,                            TRUE,
6869     EL_BDX_FIREFLY_2_RIGHT,                     -1, -1
6870   },
6871   {
6872     O_ALT_FIREFLY_4,                            TRUE,
6873     EL_BDX_FIREFLY_2_DOWN,                      -1, -1
6874   },
6875   {
6876     O_BUTTER_1,                                 TRUE,
6877     EL_BDX_BUTTERFLY_1_LEFT,                    -1, -1
6878   },
6879   {
6880     O_BUTTER_2,                                 TRUE,
6881     EL_BDX_BUTTERFLY_1_UP,                      -1, -1
6882   },
6883   {
6884     O_BUTTER_3,                                 TRUE,
6885     EL_BDX_BUTTERFLY_1_RIGHT,                   -1, -1
6886   },
6887   {
6888     O_BUTTER_4,                                 TRUE,
6889     EL_BDX_BUTTERFLY_1_DOWN,                    -1, -1
6890   },
6891   {
6892     O_ALT_BUTTER_1,                             TRUE,
6893     EL_BDX_BUTTERFLY_2_LEFT,                    -1, -1
6894   },
6895   {
6896     O_ALT_BUTTER_2,                             TRUE,
6897     EL_BDX_BUTTERFLY_2_UP,                      -1, -1
6898   },
6899   {
6900     O_ALT_BUTTER_3,                             TRUE,
6901     EL_BDX_BUTTERFLY_2_RIGHT,                   -1, -1
6902   },
6903   {
6904     O_ALT_BUTTER_4,                             TRUE,
6905     EL_BDX_BUTTERFLY_2_DOWN,                    -1, -1
6906   },
6907   {
6908     O_STONEFLY_1,                               TRUE,
6909     EL_BDX_STONEFLY_LEFT,                       -1, -1
6910   },
6911   {
6912     O_STONEFLY_2,                               TRUE,
6913     EL_BDX_STONEFLY_UP,                         -1, -1
6914   },
6915   {
6916     O_STONEFLY_3,                               TRUE,
6917     EL_BDX_STONEFLY_RIGHT,                      -1, -1
6918   },
6919   {
6920     O_STONEFLY_4,                               TRUE,
6921     EL_BDX_STONEFLY_DOWN,                       -1, -1
6922   },
6923   {
6924     O_BITER_1,                                  TRUE,
6925     EL_BDX_BITER_UP,                            -1, -1
6926   },
6927   {
6928     O_BITER_2,                                  TRUE,
6929     EL_BDX_BITER_RIGHT,                         -1, -1
6930   },
6931   {
6932     O_BITER_3,                                  TRUE,
6933     EL_BDX_BITER_DOWN,                          -1, -1
6934   },
6935   {
6936     O_BITER_4,                                  TRUE,
6937     EL_BDX_BITER_LEFT,                          -1, -1
6938   },
6939   {
6940     O_DRAGONFLY_1,                              TRUE,
6941     EL_BDX_DRAGONFLY_LEFT,                      -1, -1
6942   },
6943   {
6944     O_DRAGONFLY_2,                              TRUE,
6945     EL_BDX_DRAGONFLY_UP,                        -1, -1
6946   },
6947   {
6948     O_DRAGONFLY_3,                              TRUE,
6949     EL_BDX_DRAGONFLY_RIGHT,                     -1, -1
6950   },
6951   {
6952     O_DRAGONFLY_4,                              TRUE,
6953     EL_BDX_DRAGONFLY_DOWN,                      -1, -1
6954   },
6955   {
6956     O_PRE_PL_1,                                 TRUE,
6957     EL_BDX_PLAYER_GROWING_1,                    -1, -1
6958   },
6959   {
6960     O_PRE_PL_1,                                 FALSE,
6961     EL_BDX_PLAYER,                              ACTION_GROWING, -1
6962   },
6963   {
6964     O_PRE_PL_2,                                 TRUE,
6965     EL_BDX_PLAYER_GROWING_2,                    -1, -1
6966   },
6967   {
6968     O_PRE_PL_2,                                 FALSE,
6969     EL_BDX_PLAYER,                              ACTION_GROWING, -1
6970   },
6971   {
6972     O_PRE_PL_3,                                 TRUE,
6973     EL_BDX_PLAYER_GROWING_3,                    -1, -1
6974   },
6975   {
6976     O_PRE_PL_3,                                 FALSE,
6977     EL_BDX_PLAYER,                              ACTION_GROWING, -1
6978   },
6979   {
6980     O_PLAYER,                                   TRUE,
6981     EL_BDX_PLAYER,                              -1, -1
6982   },
6983   {
6984     O_PLAYER_BOMB,                              TRUE,
6985     EL_BDX_PLAYER_WITH_BOMB,                    -1, -1
6986   },
6987   {
6988     O_PLAYER_ROCKET_LAUNCHER,                   TRUE,
6989     EL_BDX_PLAYER_WITH_ROCKET_LAUNCHER,         -1, -1
6990   },
6991   {
6992     O_PLAYER_GLUED,                             TRUE,
6993     EL_BDX_PLAYER_GLUED,                        -1, -1
6994   },
6995   {
6996     O_PLAYER_STIRRING,                          TRUE,
6997     EL_BDX_PLAYER_STIRRING,                     -1, -1
6998   },
6999   {
7000     O_ROCKET_LAUNCHER,                          TRUE,
7001     EL_BDX_ROCKET_LAUNCHER,                     -1, -1
7002   },
7003   {
7004     O_ROCKET_1,                                 TRUE,
7005     EL_BDX_ROCKET_RIGHT,                        -1, -1
7006   },
7007   {
7008     O_ROCKET_2,                                 TRUE,
7009     EL_BDX_ROCKET_UP,                           -1, -1
7010   },
7011   {
7012     O_ROCKET_3,                                 TRUE,
7013     EL_BDX_ROCKET_LEFT,                         -1, -1
7014   },
7015   {
7016     O_ROCKET_4,                                 TRUE,
7017     EL_BDX_ROCKET_DOWN,                         -1, -1
7018   },
7019   {
7020     O_BOMB,                                     TRUE,
7021     EL_BDX_BOMB,                                -1, -1
7022   },
7023   {
7024     O_BOMB_TICK_1,                              TRUE,
7025     EL_BDX_BOMB_TICKING_1,                      -1, -1
7026   },
7027   {
7028     O_BOMB_TICK_1,                              FALSE,
7029     EL_BDX_BOMB,                                ACTION_ACTIVE, -1
7030   },
7031   {
7032     O_BOMB_TICK_2,                              TRUE,
7033     EL_BDX_BOMB_TICKING_2,                      -1, -1
7034   },
7035   {
7036     O_BOMB_TICK_2,                              FALSE,
7037     EL_BDX_BOMB,                                ACTION_ACTIVE, -1
7038   },
7039   {
7040     O_BOMB_TICK_3,                              TRUE,
7041     EL_BDX_BOMB_TICKING_3,                      -1, -1
7042   },
7043   {
7044     O_BOMB_TICK_3,                              FALSE,
7045     EL_BDX_BOMB,                                ACTION_ACTIVE, -1
7046   },
7047   {
7048     O_BOMB_TICK_4,                              TRUE,
7049     EL_BDX_BOMB_TICKING_4,                      -1, -1
7050   },
7051   {
7052     O_BOMB_TICK_4,                              FALSE,
7053     EL_BDX_BOMB,                                ACTION_ACTIVE, -1
7054   },
7055   {
7056     O_BOMB_TICK_5,                              TRUE,
7057     EL_BDX_BOMB_TICKING_5,                      -1, -1
7058   },
7059   {
7060     O_BOMB_TICK_5,                              FALSE,
7061     EL_BDX_BOMB,                                ACTION_ACTIVE, -1
7062   },
7063   {
7064     O_BOMB_TICK_6,                              TRUE,
7065     EL_BDX_BOMB_TICKING_6,                      -1, -1
7066   },
7067   {
7068     O_BOMB_TICK_6,                              FALSE,
7069     EL_BDX_BOMB,                                ACTION_ACTIVE, -1
7070   },
7071   {
7072     O_BOMB_TICK_7,                              TRUE,
7073     EL_BDX_BOMB_TICKING_7,                      -1, -1
7074   },
7075   {
7076     O_BOMB_TICK_7,                              FALSE,
7077     EL_BDX_BOMB,                                ACTION_ACTIVE, -1
7078   },
7079   {
7080     O_NITRO_PACK,                               TRUE,
7081     EL_BDX_NITRO_PACK,                          -1, -1
7082   },
7083   {
7084     O_NITRO_PACK_F,                             TRUE,
7085     EL_BDX_NITRO_PACK_FALLING,                  -1, -1
7086   },
7087   {
7088     O_NITRO_PACK_F,                             FALSE,
7089     EL_BDX_NITRO_PACK,                          ACTION_FALLING, -1
7090   },
7091   {
7092     O_PRE_CLOCK_1,                              TRUE,
7093     EL_BDX_CLOCK_GROWING_1,                     -1, -1
7094   },
7095   {
7096     O_PRE_CLOCK_1,                              FALSE,
7097     EL_BDX_CLOCK,                               ACTION_GROWING, -1
7098   },
7099   {
7100     O_PRE_CLOCK_2,                              TRUE,
7101     EL_BDX_CLOCK_GROWING_2,                     -1, -1
7102   },
7103   {
7104     O_PRE_CLOCK_2,                              FALSE,
7105     EL_BDX_CLOCK,                               ACTION_GROWING, -1
7106   },
7107   {
7108     O_PRE_CLOCK_3,                              TRUE,
7109     EL_BDX_CLOCK_GROWING_3,                     -1, -1
7110   },
7111   {
7112     O_PRE_CLOCK_3,                              FALSE,
7113     EL_BDX_CLOCK,                               ACTION_GROWING, -1
7114   },
7115   {
7116     O_PRE_CLOCK_4,                              TRUE,
7117     EL_BDX_CLOCK_GROWING_4,                     -1, -1
7118   },
7119   {
7120     O_PRE_CLOCK_4,                              FALSE,
7121     EL_BDX_CLOCK,                               ACTION_GROWING, -1
7122   },
7123   {
7124     O_PRE_DIA_1,                                TRUE,
7125     EL_BDX_DIAMOND_GROWING_1,                   -1, -1
7126   },
7127   {
7128     O_PRE_DIA_1,                                FALSE,
7129     EL_BDX_DIAMOND,                             ACTION_GROWING, -1
7130   },
7131   {
7132     O_PRE_DIA_2,                                TRUE,
7133     EL_BDX_DIAMOND_GROWING_2,                   -1, -1
7134   },
7135   {
7136     O_PRE_DIA_2,                                FALSE,
7137     EL_BDX_DIAMOND,                             ACTION_GROWING, -1
7138   },
7139   {
7140     O_PRE_DIA_3,                                TRUE,
7141     EL_BDX_DIAMOND_GROWING_3,                   -1, -1
7142   },
7143   {
7144     O_PRE_DIA_3,                                FALSE,
7145     EL_BDX_DIAMOND,                             ACTION_GROWING, -1
7146   },
7147   {
7148     O_PRE_DIA_4,                                TRUE,
7149     EL_BDX_DIAMOND_GROWING_4,                   -1, -1
7150   },
7151   {
7152     O_PRE_DIA_4,                                FALSE,
7153     EL_BDX_DIAMOND,                             ACTION_GROWING, -1
7154   },
7155   {
7156     O_PRE_DIA_5,                                TRUE,
7157     EL_BDX_DIAMOND_GROWING_5,                   -1, -1
7158   },
7159   {
7160     O_PRE_DIA_5,                                FALSE,
7161     EL_BDX_DIAMOND,                             ACTION_GROWING, -1
7162   },
7163   {
7164     O_EXPLODE_1,                                TRUE,
7165     EL_BDX_EXPLODING_1,                         -1, -1
7166   },
7167   {
7168     O_EXPLODE_1,                                FALSE,
7169     EL_BDX_DEFAULT,                             ACTION_EXPLODING, -1
7170   },
7171   {
7172     O_EXPLODE_2,                                TRUE,
7173     EL_BDX_EXPLODING_2,                         -1, -1
7174   },
7175   {
7176     O_EXPLODE_2,                                FALSE,
7177     EL_BDX_DEFAULT,                             ACTION_EXPLODING, -1
7178   },
7179   {
7180     O_EXPLODE_3,                                TRUE,
7181     EL_BDX_EXPLODING_3,                         -1, -1
7182   },
7183   {
7184     O_EXPLODE_3,                                FALSE,
7185     EL_BDX_DEFAULT,                             ACTION_EXPLODING, -1
7186   },
7187   {
7188     O_EXPLODE_4,                                TRUE,
7189     EL_BDX_EXPLODING_4,                         -1, -1
7190   },
7191   {
7192     O_EXPLODE_4,                                FALSE,
7193     EL_BDX_DEFAULT,                             ACTION_EXPLODING, -1
7194   },
7195   {
7196     O_EXPLODE_5,                                TRUE,
7197     EL_BDX_EXPLODING_5,                         -1, -1
7198   },
7199   {
7200     O_EXPLODE_5,                                FALSE,
7201     EL_BDX_DEFAULT,                             ACTION_EXPLODING, -1
7202   },
7203   {
7204     O_PRE_STONE_1,                              TRUE,
7205     EL_BDX_ROCK_GROWING_1,                      -1, -1
7206   },
7207   {
7208     O_PRE_STONE_1,                              FALSE,
7209     EL_BDX_ROCK,                                ACTION_GROWING, -1
7210   },
7211   {
7212     O_PRE_STONE_2,                              TRUE,
7213     EL_BDX_ROCK_GROWING_2,                      -1, -1
7214   },
7215   {
7216     O_PRE_STONE_2,                              FALSE,
7217     EL_BDX_ROCK,                                ACTION_GROWING, -1
7218   },
7219   {
7220     O_PRE_STONE_3,                              TRUE,
7221     EL_BDX_ROCK_GROWING_3,                      -1, -1
7222   },
7223   {
7224     O_PRE_STONE_3,                              FALSE,
7225     EL_BDX_ROCK,                                ACTION_GROWING, -1
7226   },
7227   {
7228     O_PRE_STONE_4,                              TRUE,
7229     EL_BDX_ROCK_GROWING_4,                      -1, -1
7230   },
7231   {
7232     O_PRE_STONE_4,                              FALSE,
7233     EL_BDX_ROCK,                                ACTION_GROWING, -1
7234   },
7235   {
7236     O_PRE_STEEL_1,                              TRUE,
7237     EL_BDX_STEELWALL_GROWING_1,                 -1, -1
7238   },
7239   {
7240     O_PRE_STEEL_1,                              FALSE,
7241     EL_BDX_STEELWALL,                           ACTION_GROWING, -1
7242   },
7243   {
7244     O_PRE_STEEL_2,                              TRUE,
7245     EL_BDX_STEELWALL_GROWING_2,                 -1, -1
7246   },
7247   {
7248     O_PRE_STEEL_2,                              FALSE,
7249     EL_BDX_STEELWALL,                           ACTION_GROWING, -1
7250   },
7251   {
7252     O_PRE_STEEL_3,                              TRUE,
7253     EL_BDX_STEELWALL_GROWING_3,                 -1, -1
7254   },
7255   {
7256     O_PRE_STEEL_3,                              FALSE,
7257     EL_BDX_STEELWALL,                           ACTION_GROWING, -1
7258   },
7259   {
7260     O_PRE_STEEL_4,                              TRUE,
7261     EL_BDX_STEELWALL_GROWING_4,                 -1, -1
7262   },
7263   {
7264     O_PRE_STEEL_4,                              FALSE,
7265     EL_BDX_STEELWALL,                           ACTION_GROWING, -1
7266   },
7267   {
7268     O_GHOST_EXPL_1,                             TRUE,
7269     EL_BDX_GHOST_EXPLODING_1,                   -1, -1
7270   },
7271   {
7272     O_GHOST_EXPL_1,                             FALSE,
7273     EL_BDX_GHOST,                               ACTION_EXPLODING, -1
7274   },
7275   {
7276     O_GHOST_EXPL_2,                             TRUE,
7277     EL_BDX_GHOST_EXPLODING_2,                   -1, -1
7278   },
7279   {
7280     O_GHOST_EXPL_2,                             FALSE,
7281     EL_BDX_GHOST,                               ACTION_EXPLODING, -1
7282   },
7283   {
7284     O_GHOST_EXPL_3,                             TRUE,
7285     EL_BDX_GHOST_EXPLODING_3,                   -1, -1
7286   },
7287   {
7288     O_GHOST_EXPL_3,                             FALSE,
7289     EL_BDX_GHOST,                               ACTION_EXPLODING, -1
7290   },
7291   {
7292     O_GHOST_EXPL_4,                             TRUE,
7293     EL_BDX_GHOST_EXPLODING_4,                   -1, -1
7294   },
7295   {
7296     O_GHOST_EXPL_4,                             FALSE,
7297     EL_BDX_GHOST,                               ACTION_EXPLODING, -1
7298   },
7299   {
7300     O_BOMB_EXPL_1,                              TRUE,
7301     EL_BDX_BOMB_EXPLODING_1,                    -1, -1
7302   },
7303   {
7304     O_BOMB_EXPL_1,                              FALSE,
7305     EL_BDX_BOMB,                                ACTION_EXPLODING, -1
7306   },
7307   {
7308     O_BOMB_EXPL_2,                              TRUE,
7309     EL_BDX_BOMB_EXPLODING_2,                    -1, -1
7310   },
7311   {
7312     O_BOMB_EXPL_2,                              FALSE,
7313     EL_BDX_BOMB,                                ACTION_EXPLODING, -1
7314   },
7315   {
7316     O_BOMB_EXPL_3,                              TRUE,
7317     EL_BDX_BOMB_EXPLODING_3,                    -1, -1
7318   },
7319   {
7320     O_BOMB_EXPL_3,                              FALSE,
7321     EL_BDX_BOMB,                                ACTION_EXPLODING, -1
7322   },
7323   {
7324     O_BOMB_EXPL_4,                              TRUE,
7325     EL_BDX_BOMB_EXPLODING_4,                    -1, -1
7326   },
7327   {
7328     O_BOMB_EXPL_4,                              FALSE,
7329     EL_BDX_BOMB,                                ACTION_EXPLODING, -1
7330   },
7331   {
7332     O_NITRO_EXPL_1,                             TRUE,
7333     EL_BDX_NITRO_PACK_EXPLODING_1,              -1, -1
7334   },
7335   {
7336     O_NITRO_EXPL_1,                             FALSE,
7337     EL_BDX_NITRO_PACK,                          ACTION_EXPLODING, -1
7338   },
7339   {
7340     O_NITRO_EXPL_2,                             TRUE,
7341     EL_BDX_NITRO_PACK_EXPLODING_2,              -1, -1
7342   },
7343   {
7344     O_NITRO_EXPL_2,                             FALSE,
7345     EL_BDX_NITRO_PACK,                          ACTION_EXPLODING, -1
7346   },
7347   {
7348     O_NITRO_EXPL_3,                             TRUE,
7349     EL_BDX_NITRO_PACK_EXPLODING_3,              -1, -1
7350   },
7351   {
7352     O_NITRO_EXPL_3,                             FALSE,
7353     EL_BDX_NITRO_PACK,                          ACTION_EXPLODING, -1
7354   },
7355   {
7356     O_NITRO_EXPL_4,                             TRUE,
7357     EL_BDX_NITRO_PACK_EXPLODING_4,              -1, -1
7358   },
7359   {
7360     O_NITRO_EXPL_4,                             FALSE,
7361     EL_BDX_NITRO_PACK,                          ACTION_EXPLODING, -1
7362   },
7363   {
7364     O_NITRO_PACK_EXPLODE,                       TRUE,
7365     EL_BDX_NITRO_PACK_EXPLODING,                -1, -1
7366   },
7367   {
7368     O_NITRO_PACK_EXPLODE,                       FALSE,
7369     EL_BDX_NITRO_PACK,                          ACTION_EXPLODING, -1
7370   },
7371   {
7372     O_AMOEBA_2_EXPL_1,                          TRUE,
7373     EL_BDX_AMOEBA_2_EXPLODING_1,                -1, -1
7374   },
7375   {
7376     O_AMOEBA_2_EXPL_1,                          FALSE,
7377     EL_BDX_AMOEBA_2,                            ACTION_EXPLODING, -1
7378   },
7379   {
7380     O_AMOEBA_2_EXPL_2,                          TRUE,
7381     EL_BDX_AMOEBA_2_EXPLODING_2,                -1, -1
7382   },
7383   {
7384     O_AMOEBA_2_EXPL_2,                          FALSE,
7385     EL_BDX_AMOEBA_2,                            ACTION_EXPLODING, -1
7386   },
7387   {
7388     O_AMOEBA_2_EXPL_3,                          TRUE,
7389     EL_BDX_AMOEBA_2_EXPLODING_3,                -1, -1
7390   },
7391   {
7392     O_AMOEBA_2_EXPL_3,                          FALSE,
7393     EL_BDX_AMOEBA_2,                            ACTION_EXPLODING, -1
7394   },
7395   {
7396     O_AMOEBA_2_EXPL_4,                          TRUE,
7397     EL_BDX_AMOEBA_2_EXPLODING_4,                -1, -1
7398   },
7399   {
7400     O_AMOEBA_2_EXPL_4,                          FALSE,
7401     EL_BDX_AMOEBA_2,                            ACTION_EXPLODING, -1
7402   },
7403   {
7404     O_NUT_EXPL_1,                               TRUE,
7405     EL_BDX_NUT_BREAKING_1,                      -1, -1
7406   },
7407   {
7408     O_NUT_EXPL_1,                               FALSE,
7409     EL_BDX_NUT,                                 ACTION_BREAKING, -1
7410   },
7411   {
7412     O_NUT_EXPL_2,                               TRUE,
7413     EL_BDX_NUT_BREAKING_2,                      -1, -1
7414   },
7415   {
7416     O_NUT_EXPL_2,                               FALSE,
7417     EL_BDX_NUT,                                 ACTION_BREAKING, -1
7418   },
7419   {
7420     O_NUT_EXPL_3,                               TRUE,
7421     EL_BDX_NUT_BREAKING_3,                      -1, -1
7422   },
7423   {
7424     O_NUT_EXPL_3,                               FALSE,
7425     EL_BDX_NUT,                                 ACTION_BREAKING, -1
7426   },
7427   {
7428     O_NUT_EXPL_4,                               TRUE,
7429     EL_BDX_NUT_BREAKING_4,                      -1, -1
7430   },
7431   {
7432     O_NUT_EXPL_4,                               FALSE,
7433     EL_BDX_NUT,                                 ACTION_BREAKING, -1
7434   },
7435   {
7436     O_PLAYER_PNEUMATIC_LEFT,                    FALSE,
7437     EL_BDX_PLAYER,                              ACTION_HITTING, MV_BIT_LEFT
7438   },
7439   {
7440     O_PLAYER_PNEUMATIC_RIGHT,                   FALSE,
7441     EL_BDX_PLAYER,                              ACTION_HITTING, MV_BIT_RIGHT
7442   },
7443   {
7444     O_PNEUMATIC_ACTIVE_LEFT,                    FALSE,
7445     EL_BDX_PNEUMATIC_HAMMER,                    ACTION_HITTING, MV_BIT_LEFT
7446   },
7447   {
7448     O_PNEUMATIC_ACTIVE_RIGHT,                   FALSE,
7449     EL_BDX_PNEUMATIC_HAMMER,                    ACTION_HITTING, MV_BIT_RIGHT
7450   },
7451
7452   // helper (runtime) elements
7453
7454   {
7455     O_FAKE_BONUS,                               FALSE,
7456     EL_BDX_FAKE_BONUS,                          -1, -1
7457   },
7458   {
7459     O_INBOX_CLOSED,                             FALSE,
7460     EL_BDX_INBOX,                               -1, -1
7461   },
7462   {
7463     O_INBOX_OPEN,                               FALSE,
7464     EL_BDX_INBOX,                               ACTION_OPENING, -1
7465   },
7466   {
7467     O_OUTBOX_CLOSED,                            FALSE,
7468     EL_BDX_EXIT_CLOSED,                         -1, -1
7469   },
7470   {
7471     O_OUTBOX_OPEN,                              FALSE,
7472     EL_BDX_EXIT_OPEN,                           -1, -1
7473   },
7474   {
7475     O_COVERED,                                  FALSE,
7476     EL_BDX_COVERED,                             -1, -1
7477   },
7478   {
7479     O_PLAYER_LEFT,                              FALSE,
7480     EL_BDX_PLAYER,                              ACTION_MOVING, MV_BIT_LEFT
7481   },
7482   {
7483     O_PLAYER_RIGHT,                             FALSE,
7484     EL_BDX_PLAYER,                              ACTION_MOVING, MV_BIT_RIGHT
7485   },
7486   {
7487     O_PLAYER_UP,                                FALSE,
7488     EL_BDX_PLAYER,                              ACTION_MOVING, MV_BIT_UP
7489   },
7490   {
7491     O_PLAYER_DOWN,                              FALSE,
7492     EL_BDX_PLAYER,                              ACTION_MOVING, MV_BIT_DOWN
7493   },
7494   {
7495     O_PLAYER_BLINK,                             FALSE,
7496     EL_BDX_PLAYER,                              ACTION_BORING_1, -1
7497   },
7498   {
7499     O_PLAYER_TAP,                               FALSE,
7500     EL_BDX_PLAYER,                              ACTION_BORING_2, -1
7501   },
7502   {
7503     O_PLAYER_TAP_BLINK,                         FALSE,
7504     EL_BDX_PLAYER,                              ACTION_BORING_3, -1
7505   },
7506   {
7507     O_PLAYER_PUSH_LEFT,                         FALSE,
7508     EL_BDX_PLAYER,                              ACTION_PUSHING, MV_BIT_LEFT
7509   },
7510   {
7511     O_PLAYER_PUSH_RIGHT,                        FALSE,
7512     EL_BDX_PLAYER,                              ACTION_PUSHING, MV_BIT_RIGHT
7513   },
7514   {
7515     O_CREATURE_SWITCH_ON,                       FALSE,
7516     EL_BDX_CREATURE_SWITCH_ACTIVE,              -1, -1
7517   },
7518   {
7519     O_EXPANDING_WALL_SWITCH_HORIZ,              FALSE,
7520     EL_BDX_EXPANDABLE_WALL_SWITCH,              -1, -1
7521   },
7522   {
7523     O_EXPANDING_WALL_SWITCH_VERT,               FALSE,
7524     EL_BDX_EXPANDABLE_WALL_SWITCH_ACTIVE,       -1, -1
7525   },
7526   {
7527     O_GRAVITY_SWITCH_ACTIVE,                    FALSE,
7528     EL_BDX_GRAVITY_SWITCH_ACTIVE,               -1, -1
7529   },
7530   {
7531     O_REPLICATOR_SWITCH_OFF,                    FALSE,
7532     EL_BDX_REPLICATOR_SWITCH,                   -1, -1
7533   },
7534   {
7535     O_REPLICATOR_SWITCH_ON,                     FALSE,
7536     EL_BDX_REPLICATOR_SWITCH_ACTIVE,            -1, -1
7537   },
7538   {
7539     O_CONVEYOR_DIR_NORMAL,                      FALSE,
7540     EL_BDX_CONVEYOR_DIR_SWITCH,                 -1, -1
7541   },
7542   {
7543     O_CONVEYOR_DIR_CHANGED,                     FALSE,
7544     EL_BDX_CONVEYOR_DIR_SWITCH_ACTIVE,          -1, -1
7545   },
7546   {
7547     O_CONVEYOR_SWITCH_OFF,                      FALSE,
7548     EL_BDX_CONVEYOR_SWITCH,                     -1, -1
7549   },
7550   {
7551     O_CONVEYOR_SWITCH_ON,                       FALSE,
7552     EL_BDX_CONVEYOR_SWITCH_ACTIVE,              -1, -1
7553   },
7554   {
7555     O_MAGIC_WALL_ACTIVE,                        FALSE,
7556     EL_BDX_MAGIC_WALL_ACTIVE,           -1, -1
7557   },
7558   {
7559     O_REPLICATOR_ACTIVE,                        FALSE,
7560     EL_BDX_REPLICATOR_ACTIVE,                   -1, -1
7561   },
7562   {
7563     O_CONVEYOR_LEFT_ACTIVE,                     FALSE,
7564     EL_BDX_CONVEYOR_LEFT_ACTIVE,                -1, -1
7565   },
7566   {
7567     O_CONVEYOR_RIGHT_ACTIVE,                    FALSE,
7568     EL_BDX_CONVEYOR_RIGHT_ACTIVE,               -1, -1
7569   },
7570   {
7571     O_BITER_SWITCH_1,                           FALSE,
7572     EL_BDX_BITER_SWITCH_1,                      -1, -1
7573   },
7574   {
7575     O_BITER_SWITCH_2,                           FALSE,
7576     EL_BDX_BITER_SWITCH_2,                      -1, -1
7577   },
7578   {
7579     O_BITER_SWITCH_3,                           FALSE,
7580     EL_BDX_BITER_SWITCH_3,                      -1, -1
7581   },
7582   {
7583     O_BITER_SWITCH_4,                           FALSE,
7584     EL_BDX_BITER_SWITCH_4,                      -1, -1
7585   },
7586
7587   {
7588     -1,                                         FALSE,
7589     -1,                                         -1, -1
7590   }
7591 };
7592
7593 int map_element_RND_to_BD_cave(int element_rnd)
7594 {
7595   static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS];
7596   static boolean mapping_initialized = FALSE;
7597
7598   if (!mapping_initialized)
7599   {
7600     int i;
7601
7602     // return "O_UNKNOWN" for all undefined elements in mapping array
7603     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7604       mapping_RND_to_BD[i] = O_UNKNOWN;
7605
7606     for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7607       if (bd_object_mapping_list[i].is_rnd_to_bd_mapping)
7608         mapping_RND_to_BD[bd_object_mapping_list[i].element_rnd] =
7609           bd_object_mapping_list[i].element_bd;
7610
7611     mapping_initialized = TRUE;
7612   }
7613
7614   if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7615   {
7616     Warn("invalid RND element %d", element_rnd);
7617
7618     return O_UNKNOWN;
7619   }
7620
7621   return mapping_RND_to_BD[element_rnd];
7622 }
7623
7624 int map_element_RND_to_BD_effect(int element_rnd, int action)
7625 {
7626   static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS][NUM_ACTIONS];
7627   static boolean mapping_initialized = FALSE;
7628
7629   if (!mapping_initialized)
7630   {
7631     int i, j;
7632
7633     // return "O_UNKNOWN" for all undefined elements in mapping array
7634     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7635       for (j = 0; j < NUM_ACTIONS; j++)
7636         mapping_RND_to_BD[i][j] = O_UNKNOWN;
7637
7638     for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7639     {
7640       int element_rnd = bd_object_mapping_list[i].element_rnd;
7641       int element_bd  = bd_object_mapping_list[i].element_bd;
7642       int action      = bd_object_mapping_list[i].action;
7643
7644       if (action != -1)
7645         mapping_RND_to_BD[element_rnd][action] = element_bd;
7646     }
7647
7648     mapping_initialized = TRUE;
7649   }
7650
7651   if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7652   {
7653     Warn("invalid RND element %d", element_rnd);
7654
7655     return O_UNKNOWN;
7656   }
7657
7658   if (action < 0 || action >= NUM_ACTIONS)
7659   {
7660     Warn("invalid action %d", action);
7661
7662     return O_UNKNOWN;
7663   }
7664
7665   return mapping_RND_to_BD[element_rnd][action];
7666 }
7667
7668 int map_element_BD_to_RND_cave(int element_bd)
7669 {
7670   static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7671   static boolean mapping_initialized = FALSE;
7672
7673   if (!mapping_initialized)
7674   {
7675     int i;
7676
7677     // return "EL_UNKNOWN" for all undefined elements in mapping array
7678     for (i = 0; i < O_MAX_ALL; i++)
7679       mapping_BD_to_RND[i] = EL_UNKNOWN;
7680
7681     for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7682       if (bd_object_mapping_list[i].is_rnd_to_bd_mapping)
7683         mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7684           bd_object_mapping_list[i].element_rnd;
7685
7686     mapping_initialized = TRUE;
7687   }
7688
7689   // always map (scanned) run-time elements to normal elements
7690   element_bd &= O_MASK;
7691
7692   if (element_bd < 0 || element_bd >= O_MAX_ALL)
7693   {
7694     Warn("invalid BD element %d", element_bd);
7695
7696     return EL_UNKNOWN;
7697   }
7698
7699   return mapping_BD_to_RND[element_bd];
7700 }
7701
7702 int map_element_BD_to_RND_game(int element_bd)
7703 {
7704   static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7705   static boolean mapping_initialized = FALSE;
7706
7707   if (!mapping_initialized)
7708   {
7709     int i;
7710
7711     // return "EL_UNKNOWN" for all undefined elements in mapping array
7712     for (i = 0; i < O_MAX_ALL; i++)
7713       mapping_BD_to_RND[i] = EL_UNKNOWN;
7714
7715     for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7716       mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7717         bd_object_mapping_list[i].element_rnd;
7718
7719     mapping_initialized = TRUE;
7720   }
7721
7722   // always map (scanned) run-time elements to normal elements
7723   element_bd &= O_MASK;
7724
7725   if (element_bd < 0 || element_bd >= O_MAX_ALL)
7726   {
7727     Warn("invalid BD element %d", element_bd);
7728
7729     return EL_UNKNOWN;
7730   }
7731
7732   return mapping_BD_to_RND[element_bd];
7733 }
7734
7735 static struct Mapping_EM_to_RND_object
7736 {
7737   int element_em;
7738   boolean is_rnd_to_em_mapping;         // unique mapping EM <-> RND
7739   boolean is_backside;                  // backside of moving element
7740
7741   int element_rnd;
7742   int action;
7743   int direction;
7744 }
7745 em_object_mapping_list[GAME_TILE_MAX + 1] =
7746 {
7747   {
7748     Zborder,                            FALSE,  FALSE,
7749     EL_EMPTY,                           -1, -1
7750   },
7751   {
7752     Zplayer,                            FALSE,  FALSE,
7753     EL_EMPTY,                           -1, -1
7754   },
7755
7756   {
7757     Zbug,                               FALSE,  FALSE,
7758     EL_EMPTY,                           -1, -1
7759   },
7760   {
7761     Ztank,                              FALSE,  FALSE,
7762     EL_EMPTY,                           -1, -1
7763   },
7764   {
7765     Zeater,                             FALSE,  FALSE,
7766     EL_EMPTY,                           -1, -1
7767   },
7768   {
7769     Zdynamite,                          FALSE,  FALSE,
7770     EL_EMPTY,                           -1, -1
7771   },
7772   {
7773     Zboom,                              FALSE,  FALSE,
7774     EL_EMPTY,                           -1, -1
7775   },
7776
7777   {
7778     Xchain,                             FALSE,  FALSE,
7779     EL_DEFAULT,                         ACTION_EXPLODING, -1
7780   },
7781   {
7782     Xboom_bug,                          FALSE,  FALSE,
7783     EL_BUG,                             ACTION_EXPLODING, -1
7784   },
7785   {
7786     Xboom_tank,                         FALSE,  FALSE,
7787     EL_SPACESHIP,                       ACTION_EXPLODING, -1
7788   },
7789   {
7790     Xboom_android,                      FALSE,  FALSE,
7791     EL_EMC_ANDROID,                     ACTION_OTHER, -1
7792   },
7793   {
7794     Xboom_1,                            FALSE,  FALSE,
7795     EL_DEFAULT,                         ACTION_EXPLODING, -1
7796   },
7797   {
7798     Xboom_2,                            FALSE,  FALSE,
7799     EL_DEFAULT,                         ACTION_EXPLODING, -1
7800   },
7801
7802   {
7803     Xblank,                             TRUE,   FALSE,
7804     EL_EMPTY,                           -1, -1
7805   },
7806
7807   {
7808     Xsplash_e,                          FALSE,  FALSE,
7809     EL_ACID_SPLASH_RIGHT,               -1, -1
7810   },
7811   {
7812     Xsplash_w,                          FALSE,  FALSE,
7813     EL_ACID_SPLASH_LEFT,                -1, -1
7814   },
7815
7816   {
7817     Xplant,                             TRUE,   FALSE,
7818     EL_EMC_PLANT,                       -1, -1
7819   },
7820   {
7821     Yplant,                             FALSE,  FALSE,
7822     EL_EMC_PLANT,                       -1, -1
7823   },
7824
7825   {
7826     Xacid_1,                            TRUE,   FALSE,
7827     EL_ACID,                            -1, -1
7828   },
7829   {
7830     Xacid_2,                            FALSE,  FALSE,
7831     EL_ACID,                            -1, -1
7832   },
7833   {
7834     Xacid_3,                            FALSE,  FALSE,
7835     EL_ACID,                            -1, -1
7836   },
7837   {
7838     Xacid_4,                            FALSE,  FALSE,
7839     EL_ACID,                            -1, -1
7840   },
7841   {
7842     Xacid_5,                            FALSE,  FALSE,
7843     EL_ACID,                            -1, -1
7844   },
7845   {
7846     Xacid_6,                            FALSE,  FALSE,
7847     EL_ACID,                            -1, -1
7848   },
7849   {
7850     Xacid_7,                            FALSE,  FALSE,
7851     EL_ACID,                            -1, -1
7852   },
7853   {
7854     Xacid_8,                            FALSE,  FALSE,
7855     EL_ACID,                            -1, -1
7856   },
7857
7858   {
7859     Xfake_acid_1,                       TRUE,   FALSE,
7860     EL_EMC_FAKE_ACID,                   -1, -1
7861   },
7862   {
7863     Xfake_acid_2,                       FALSE,  FALSE,
7864     EL_EMC_FAKE_ACID,                   -1, -1
7865   },
7866   {
7867     Xfake_acid_3,                       FALSE,  FALSE,
7868     EL_EMC_FAKE_ACID,                   -1, -1
7869   },
7870   {
7871     Xfake_acid_4,                       FALSE,  FALSE,
7872     EL_EMC_FAKE_ACID,                   -1, -1
7873   },
7874   {
7875     Xfake_acid_5,                       FALSE,  FALSE,
7876     EL_EMC_FAKE_ACID,                   -1, -1
7877   },
7878   {
7879     Xfake_acid_6,                       FALSE,  FALSE,
7880     EL_EMC_FAKE_ACID,                   -1, -1
7881   },
7882   {
7883     Xfake_acid_7,                       FALSE,  FALSE,
7884     EL_EMC_FAKE_ACID,                   -1, -1
7885   },
7886   {
7887     Xfake_acid_8,                       FALSE,  FALSE,
7888     EL_EMC_FAKE_ACID,                   -1, -1
7889   },
7890
7891   {
7892     Xfake_acid_1_player,                FALSE,  FALSE,
7893     EL_EMC_FAKE_ACID,                   -1, -1
7894   },
7895   {
7896     Xfake_acid_2_player,                FALSE,  FALSE,
7897     EL_EMC_FAKE_ACID,                   -1, -1
7898   },
7899   {
7900     Xfake_acid_3_player,                FALSE,  FALSE,
7901     EL_EMC_FAKE_ACID,                   -1, -1
7902   },
7903   {
7904     Xfake_acid_4_player,                FALSE,  FALSE,
7905     EL_EMC_FAKE_ACID,                   -1, -1
7906   },
7907   {
7908     Xfake_acid_5_player,                FALSE,  FALSE,
7909     EL_EMC_FAKE_ACID,                   -1, -1
7910   },
7911   {
7912     Xfake_acid_6_player,                FALSE,  FALSE,
7913     EL_EMC_FAKE_ACID,                   -1, -1
7914   },
7915   {
7916     Xfake_acid_7_player,                FALSE,  FALSE,
7917     EL_EMC_FAKE_ACID,                   -1, -1
7918   },
7919   {
7920     Xfake_acid_8_player,                FALSE,  FALSE,
7921     EL_EMC_FAKE_ACID,                   -1, -1
7922   },
7923
7924   {
7925     Xgrass,                             TRUE,   FALSE,
7926     EL_EMC_GRASS,                       -1, -1
7927   },
7928   {
7929     Ygrass_nB,                          FALSE,  FALSE,
7930     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
7931   },
7932   {
7933     Ygrass_eB,                          FALSE,  FALSE,
7934     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
7935   },
7936   {
7937     Ygrass_sB,                          FALSE,  FALSE,
7938     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
7939   },
7940   {
7941     Ygrass_wB,                          FALSE,  FALSE,
7942     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
7943   },
7944
7945   {
7946     Xdirt,                              TRUE,   FALSE,
7947     EL_SAND,                            -1, -1
7948   },
7949   {
7950     Ydirt_nB,                           FALSE,  FALSE,
7951     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
7952   },
7953   {
7954     Ydirt_eB,                           FALSE,  FALSE,
7955     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
7956   },
7957   {
7958     Ydirt_sB,                           FALSE,  FALSE,
7959     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
7960   },
7961   {
7962     Ydirt_wB,                           FALSE,  FALSE,
7963     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
7964   },
7965
7966   {
7967     Xandroid,                           TRUE,   FALSE,
7968     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
7969   },
7970   {
7971     Xandroid_1_n,                       FALSE,  FALSE,
7972     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
7973   },
7974   {
7975     Xandroid_2_n,                       FALSE,  FALSE,
7976     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
7977   },
7978   {
7979     Xandroid_1_e,                       FALSE,  FALSE,
7980     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
7981   },
7982   {
7983     Xandroid_2_e,                       FALSE,  FALSE,
7984     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
7985   },
7986   {
7987     Xandroid_1_w,                       FALSE,  FALSE,
7988     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
7989   },
7990   {
7991     Xandroid_2_w,                       FALSE,  FALSE,
7992     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
7993   },
7994   {
7995     Xandroid_1_s,                       FALSE,  FALSE,
7996     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
7997   },
7998   {
7999     Xandroid_2_s,                       FALSE,  FALSE,
8000     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
8001   },
8002   {
8003     Yandroid_n,                         FALSE,  FALSE,
8004     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
8005   },
8006   {
8007     Yandroid_nB,                        FALSE,  TRUE,
8008     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
8009   },
8010   {
8011     Yandroid_ne,                        FALSE,  FALSE,
8012     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
8013   },
8014   {
8015     Yandroid_neB,                       FALSE,  TRUE,
8016     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
8017   },
8018   {
8019     Yandroid_e,                         FALSE,  FALSE,
8020     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
8021   },
8022   {
8023     Yandroid_eB,                        FALSE,  TRUE,
8024     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
8025   },
8026   {
8027     Yandroid_se,                        FALSE,  FALSE,
8028     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
8029   },
8030   {
8031     Yandroid_seB,                       FALSE,  TRUE,
8032     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
8033   },
8034   {
8035     Yandroid_s,                         FALSE,  FALSE,
8036     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
8037   },
8038   {
8039     Yandroid_sB,                        FALSE,  TRUE,
8040     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
8041   },
8042   {
8043     Yandroid_sw,                        FALSE,  FALSE,
8044     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
8045   },
8046   {
8047     Yandroid_swB,                       FALSE,  TRUE,
8048     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
8049   },
8050   {
8051     Yandroid_w,                         FALSE,  FALSE,
8052     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
8053   },
8054   {
8055     Yandroid_wB,                        FALSE,  TRUE,
8056     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
8057   },
8058   {
8059     Yandroid_nw,                        FALSE,  FALSE,
8060     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
8061   },
8062   {
8063     Yandroid_nwB,                       FALSE,  TRUE,
8064     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
8065   },
8066
8067   {
8068     Xeater_n,                           TRUE,   FALSE,
8069     EL_YAMYAM_UP,                       -1, -1
8070   },
8071   {
8072     Xeater_e,                           TRUE,   FALSE,
8073     EL_YAMYAM_RIGHT,                    -1, -1
8074   },
8075   {
8076     Xeater_w,                           TRUE,   FALSE,
8077     EL_YAMYAM_LEFT,                     -1, -1
8078   },
8079   {
8080     Xeater_s,                           TRUE,   FALSE,
8081     EL_YAMYAM_DOWN,                     -1, -1
8082   },
8083   {
8084     Yeater_n,                           FALSE,  FALSE,
8085     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
8086   },
8087   {
8088     Yeater_nB,                          FALSE,  TRUE,
8089     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
8090   },
8091   {
8092     Yeater_e,                           FALSE,  FALSE,
8093     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
8094   },
8095   {
8096     Yeater_eB,                          FALSE,  TRUE,
8097     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
8098   },
8099   {
8100     Yeater_s,                           FALSE,  FALSE,
8101     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
8102   },
8103   {
8104     Yeater_sB,                          FALSE,  TRUE,
8105     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
8106   },
8107   {
8108     Yeater_w,                           FALSE,  FALSE,
8109     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
8110   },
8111   {
8112     Yeater_wB,                          FALSE,  TRUE,
8113     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
8114   },
8115   {
8116     Yeater_stone,                       FALSE,  FALSE,
8117     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
8118   },
8119   {
8120     Yeater_spring,                      FALSE,  FALSE,
8121     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
8122   },
8123
8124   {
8125     Xalien,                             TRUE,   FALSE,
8126     EL_ROBOT,                           -1, -1
8127   },
8128   {
8129     Xalien_pause,                       FALSE,  FALSE,
8130     EL_ROBOT,                           -1, -1
8131   },
8132   {
8133     Yalien_n,                           FALSE,  FALSE,
8134     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
8135   },
8136   {
8137     Yalien_nB,                          FALSE,  TRUE,
8138     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
8139   },
8140   {
8141     Yalien_e,                           FALSE,  FALSE,
8142     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
8143   },
8144   {
8145     Yalien_eB,                          FALSE,  TRUE,
8146     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
8147   },
8148   {
8149     Yalien_s,                           FALSE,  FALSE,
8150     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
8151   },
8152   {
8153     Yalien_sB,                          FALSE,  TRUE,
8154     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
8155   },
8156   {
8157     Yalien_w,                           FALSE,  FALSE,
8158     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
8159   },
8160   {
8161     Yalien_wB,                          FALSE,  TRUE,
8162     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
8163   },
8164   {
8165     Yalien_stone,                       FALSE,  FALSE,
8166     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
8167   },
8168   {
8169     Yalien_spring,                      FALSE,  FALSE,
8170     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
8171   },
8172
8173   {
8174     Xbug_1_n,                           TRUE,   FALSE,
8175     EL_BUG_UP,                          -1, -1
8176   },
8177   {
8178     Xbug_1_e,                           TRUE,   FALSE,
8179     EL_BUG_RIGHT,                       -1, -1
8180   },
8181   {
8182     Xbug_1_s,                           TRUE,   FALSE,
8183     EL_BUG_DOWN,                        -1, -1
8184   },
8185   {
8186     Xbug_1_w,                           TRUE,   FALSE,
8187     EL_BUG_LEFT,                        -1, -1
8188   },
8189   {
8190     Xbug_2_n,                           FALSE,  FALSE,
8191     EL_BUG_UP,                          -1, -1
8192   },
8193   {
8194     Xbug_2_e,                           FALSE,  FALSE,
8195     EL_BUG_RIGHT,                       -1, -1
8196   },
8197   {
8198     Xbug_2_s,                           FALSE,  FALSE,
8199     EL_BUG_DOWN,                        -1, -1
8200   },
8201   {
8202     Xbug_2_w,                           FALSE,  FALSE,
8203     EL_BUG_LEFT,                        -1, -1
8204   },
8205   {
8206     Ybug_n,                             FALSE,  FALSE,
8207     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
8208   },
8209   {
8210     Ybug_nB,                            FALSE,  TRUE,
8211     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
8212   },
8213   {
8214     Ybug_e,                             FALSE,  FALSE,
8215     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
8216   },
8217   {
8218     Ybug_eB,                            FALSE,  TRUE,
8219     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
8220   },
8221   {
8222     Ybug_s,                             FALSE,  FALSE,
8223     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
8224   },
8225   {
8226     Ybug_sB,                            FALSE,  TRUE,
8227     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
8228   },
8229   {
8230     Ybug_w,                             FALSE,  FALSE,
8231     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
8232   },
8233   {
8234     Ybug_wB,                            FALSE,  TRUE,
8235     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
8236   },
8237   {
8238     Ybug_w_n,                           FALSE,  FALSE,
8239     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
8240   },
8241   {
8242     Ybug_n_e,                           FALSE,  FALSE,
8243     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
8244   },
8245   {
8246     Ybug_e_s,                           FALSE,  FALSE,
8247     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
8248   },
8249   {
8250     Ybug_s_w,                           FALSE,  FALSE,
8251     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
8252   },
8253   {
8254     Ybug_e_n,                           FALSE,  FALSE,
8255     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
8256   },
8257   {
8258     Ybug_s_e,                           FALSE,  FALSE,
8259     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
8260   },
8261   {
8262     Ybug_w_s,                           FALSE,  FALSE,
8263     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
8264   },
8265   {
8266     Ybug_n_w,                           FALSE,  FALSE,
8267     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
8268   },
8269   {
8270     Ybug_stone,                         FALSE,  FALSE,
8271     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
8272   },
8273   {
8274     Ybug_spring,                        FALSE,  FALSE,
8275     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
8276   },
8277
8278   {
8279     Xtank_1_n,                          TRUE,   FALSE,
8280     EL_SPACESHIP_UP,                    -1, -1
8281   },
8282   {
8283     Xtank_1_e,                          TRUE,   FALSE,
8284     EL_SPACESHIP_RIGHT,                 -1, -1
8285   },
8286   {
8287     Xtank_1_s,                          TRUE,   FALSE,
8288     EL_SPACESHIP_DOWN,                  -1, -1
8289   },
8290   {
8291     Xtank_1_w,                          TRUE,   FALSE,
8292     EL_SPACESHIP_LEFT,                  -1, -1
8293   },
8294   {
8295     Xtank_2_n,                          FALSE,  FALSE,
8296     EL_SPACESHIP_UP,                    -1, -1
8297   },
8298   {
8299     Xtank_2_e,                          FALSE,  FALSE,
8300     EL_SPACESHIP_RIGHT,                 -1, -1
8301   },
8302   {
8303     Xtank_2_s,                          FALSE,  FALSE,
8304     EL_SPACESHIP_DOWN,                  -1, -1
8305   },
8306   {
8307     Xtank_2_w,                          FALSE,  FALSE,
8308     EL_SPACESHIP_LEFT,                  -1, -1
8309   },
8310   {
8311     Ytank_n,                            FALSE,  FALSE,
8312     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
8313   },
8314   {
8315     Ytank_nB,                           FALSE,  TRUE,
8316     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
8317   },
8318   {
8319     Ytank_e,                            FALSE,  FALSE,
8320     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
8321   },
8322   {
8323     Ytank_eB,                           FALSE,  TRUE,
8324     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
8325   },
8326   {
8327     Ytank_s,                            FALSE,  FALSE,
8328     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
8329   },
8330   {
8331     Ytank_sB,                           FALSE,  TRUE,
8332     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
8333   },
8334   {
8335     Ytank_w,                            FALSE,  FALSE,
8336     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
8337   },
8338   {
8339     Ytank_wB,                           FALSE,  TRUE,
8340     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
8341   },
8342   {
8343     Ytank_w_n,                          FALSE,  FALSE,
8344     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
8345   },
8346   {
8347     Ytank_n_e,                          FALSE,  FALSE,
8348     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
8349   },
8350   {
8351     Ytank_e_s,                          FALSE,  FALSE,
8352     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
8353   },
8354   {
8355     Ytank_s_w,                          FALSE,  FALSE,
8356     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
8357   },
8358   {
8359     Ytank_e_n,                          FALSE,  FALSE,
8360     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
8361   },
8362   {
8363     Ytank_s_e,                          FALSE,  FALSE,
8364     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
8365   },
8366   {
8367     Ytank_w_s,                          FALSE,  FALSE,
8368     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
8369   },
8370   {
8371     Ytank_n_w,                          FALSE,  FALSE,
8372     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
8373   },
8374   {
8375     Ytank_stone,                        FALSE,  FALSE,
8376     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
8377   },
8378   {
8379     Ytank_spring,                       FALSE,  FALSE,
8380     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
8381   },
8382
8383   {
8384     Xemerald,                           TRUE,   FALSE,
8385     EL_EMERALD,                         -1, -1
8386   },
8387   {
8388     Xemerald_pause,                     FALSE,  FALSE,
8389     EL_EMERALD,                         -1, -1
8390   },
8391   {
8392     Xemerald_fall,                      FALSE,  FALSE,
8393     EL_EMERALD,                         -1, -1
8394   },
8395   {
8396     Xemerald_shine,                     FALSE,  FALSE,
8397     EL_EMERALD,                         ACTION_TWINKLING, -1
8398   },
8399   {
8400     Yemerald_s,                         FALSE,  FALSE,
8401     EL_EMERALD,                         ACTION_FALLING, -1
8402   },
8403   {
8404     Yemerald_sB,                        FALSE,  TRUE,
8405     EL_EMERALD,                         ACTION_FALLING, -1
8406   },
8407   {
8408     Yemerald_e,                         FALSE,  FALSE,
8409     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
8410   },
8411   {
8412     Yemerald_eB,                        FALSE,  TRUE,
8413     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
8414   },
8415   {
8416     Yemerald_w,                         FALSE,  FALSE,
8417     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
8418   },
8419   {
8420     Yemerald_wB,                        FALSE,  TRUE,
8421     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
8422   },
8423   {
8424     Yemerald_blank,                     FALSE,  FALSE,
8425     EL_EMERALD,                         ACTION_COLLECTING, -1
8426   },
8427
8428   {
8429     Xdiamond,                           TRUE,   FALSE,
8430     EL_DIAMOND,                         -1, -1
8431   },
8432   {
8433     Xdiamond_pause,                     FALSE,  FALSE,
8434     EL_DIAMOND,                         -1, -1
8435   },
8436   {
8437     Xdiamond_fall,                      FALSE,  FALSE,
8438     EL_DIAMOND,                         -1, -1
8439   },
8440   {
8441     Xdiamond_shine,                     FALSE,  FALSE,
8442     EL_DIAMOND,                         ACTION_TWINKLING, -1
8443   },
8444   {
8445     Ydiamond_s,                         FALSE,  FALSE,
8446     EL_DIAMOND,                         ACTION_FALLING, -1
8447   },
8448   {
8449     Ydiamond_sB,                        FALSE,  TRUE,
8450     EL_DIAMOND,                         ACTION_FALLING, -1
8451   },
8452   {
8453     Ydiamond_e,                         FALSE,  FALSE,
8454     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
8455   },
8456   {
8457     Ydiamond_eB,                        FALSE,  TRUE,
8458     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
8459   },
8460   {
8461     Ydiamond_w,                         FALSE,  FALSE,
8462     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
8463   },
8464   {
8465     Ydiamond_wB,                        FALSE,  TRUE,
8466     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
8467   },
8468   {
8469     Ydiamond_blank,                     FALSE,  FALSE,
8470     EL_DIAMOND,                         ACTION_COLLECTING, -1
8471   },
8472   {
8473     Ydiamond_stone,                     FALSE,  FALSE,
8474     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
8475   },
8476
8477   {
8478     Xstone,                             TRUE,   FALSE,
8479     EL_ROCK,                            -1, -1
8480   },
8481   {
8482     Xstone_pause,                       FALSE,  FALSE,
8483     EL_ROCK,                            -1, -1
8484   },
8485   {
8486     Xstone_fall,                        FALSE,  FALSE,
8487     EL_ROCK,                            -1, -1
8488   },
8489   {
8490     Ystone_s,                           FALSE,  FALSE,
8491     EL_ROCK,                            ACTION_FALLING, -1
8492   },
8493   {
8494     Ystone_sB,                          FALSE,  TRUE,
8495     EL_ROCK,                            ACTION_FALLING, -1
8496   },
8497   {
8498     Ystone_e,                           FALSE,  FALSE,
8499     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
8500   },
8501   {
8502     Ystone_eB,                          FALSE,  TRUE,
8503     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
8504   },
8505   {
8506     Ystone_w,                           FALSE,  FALSE,
8507     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
8508   },
8509   {
8510     Ystone_wB,                          FALSE,  TRUE,
8511     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
8512   },
8513
8514   {
8515     Xbomb,                              TRUE,   FALSE,
8516     EL_BOMB,                            -1, -1
8517   },
8518   {
8519     Xbomb_pause,                        FALSE,  FALSE,
8520     EL_BOMB,                            -1, -1
8521   },
8522   {
8523     Xbomb_fall,                         FALSE,  FALSE,
8524     EL_BOMB,                            -1, -1
8525   },
8526   {
8527     Ybomb_s,                            FALSE,  FALSE,
8528     EL_BOMB,                            ACTION_FALLING, -1
8529   },
8530   {
8531     Ybomb_sB,                           FALSE,  TRUE,
8532     EL_BOMB,                            ACTION_FALLING, -1
8533   },
8534   {
8535     Ybomb_e,                            FALSE,  FALSE,
8536     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
8537   },
8538   {
8539     Ybomb_eB,                           FALSE,  TRUE,
8540     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
8541   },
8542   {
8543     Ybomb_w,                            FALSE,  FALSE,
8544     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
8545   },
8546   {
8547     Ybomb_wB,                           FALSE,  TRUE,
8548     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
8549   },
8550   {
8551     Ybomb_blank,                        FALSE,  FALSE,
8552     EL_BOMB,                            ACTION_ACTIVATING, -1
8553   },
8554
8555   {
8556     Xnut,                               TRUE,   FALSE,
8557     EL_NUT,                             -1, -1
8558   },
8559   {
8560     Xnut_pause,                         FALSE,  FALSE,
8561     EL_NUT,                             -1, -1
8562   },
8563   {
8564     Xnut_fall,                          FALSE,  FALSE,
8565     EL_NUT,                             -1, -1
8566   },
8567   {
8568     Ynut_s,                             FALSE,  FALSE,
8569     EL_NUT,                             ACTION_FALLING, -1
8570   },
8571   {
8572     Ynut_sB,                            FALSE,  TRUE,
8573     EL_NUT,                             ACTION_FALLING, -1
8574   },
8575   {
8576     Ynut_e,                             FALSE,  FALSE,
8577     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
8578   },
8579   {
8580     Ynut_eB,                            FALSE,  TRUE,
8581     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
8582   },
8583   {
8584     Ynut_w,                             FALSE,  FALSE,
8585     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
8586   },
8587   {
8588     Ynut_wB,                            FALSE,  TRUE,
8589     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
8590   },
8591   {
8592     Ynut_stone,                         FALSE,  FALSE,
8593     EL_NUT,                             ACTION_BREAKING, -1
8594   },
8595
8596   {
8597     Xspring,                            TRUE,   FALSE,
8598     EL_SPRING,                          -1, -1
8599   },
8600   {
8601     Xspring_pause,                      FALSE,  FALSE,
8602     EL_SPRING,                          -1, -1
8603   },
8604   {
8605     Xspring_e,                          TRUE,   FALSE,
8606     EL_SPRING_RIGHT,                    -1, -1
8607   },
8608   {
8609     Xspring_w,                          TRUE,   FALSE,
8610     EL_SPRING_LEFT,                     -1, -1
8611   },
8612   {
8613     Xspring_fall,                       FALSE,  FALSE,
8614     EL_SPRING,                          -1, -1
8615   },
8616   {
8617     Yspring_s,                          FALSE,  FALSE,
8618     EL_SPRING,                          ACTION_FALLING, -1
8619   },
8620   {
8621     Yspring_sB,                         FALSE,  TRUE,
8622     EL_SPRING,                          ACTION_FALLING, -1
8623   },
8624   {
8625     Yspring_e,                          FALSE,  FALSE,
8626     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
8627   },
8628   {
8629     Yspring_eB,                         FALSE,  TRUE,
8630     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
8631   },
8632   {
8633     Yspring_w,                          FALSE,  FALSE,
8634     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
8635   },
8636   {
8637     Yspring_wB,                         FALSE,  TRUE,
8638     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
8639   },
8640   {
8641     Yspring_alien_e,                    FALSE,  FALSE,
8642     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
8643   },
8644   {
8645     Yspring_alien_eB,                   FALSE,  TRUE,
8646     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
8647   },
8648   {
8649     Yspring_alien_w,                    FALSE,  FALSE,
8650     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
8651   },
8652   {
8653     Yspring_alien_wB,                   FALSE,  TRUE,
8654     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
8655   },
8656
8657   {
8658     Xpush_emerald_e,                    FALSE,  FALSE,
8659     EL_EMERALD,                         -1, MV_BIT_RIGHT
8660   },
8661   {
8662     Xpush_emerald_w,                    FALSE,  FALSE,
8663     EL_EMERALD,                         -1, MV_BIT_LEFT
8664   },
8665   {
8666     Xpush_diamond_e,                    FALSE,  FALSE,
8667     EL_DIAMOND,                         -1, MV_BIT_RIGHT
8668   },
8669   {
8670     Xpush_diamond_w,                    FALSE,  FALSE,
8671     EL_DIAMOND,                         -1, MV_BIT_LEFT
8672   },
8673   {
8674     Xpush_stone_e,                      FALSE,  FALSE,
8675     EL_ROCK,                            -1, MV_BIT_RIGHT
8676   },
8677   {
8678     Xpush_stone_w,                      FALSE,  FALSE,
8679     EL_ROCK,                            -1, MV_BIT_LEFT
8680   },
8681   {
8682     Xpush_bomb_e,                       FALSE,  FALSE,
8683     EL_BOMB,                            -1, MV_BIT_RIGHT
8684   },
8685   {
8686     Xpush_bomb_w,                       FALSE,  FALSE,
8687     EL_BOMB,                            -1, MV_BIT_LEFT
8688   },
8689   {
8690     Xpush_nut_e,                        FALSE,  FALSE,
8691     EL_NUT,                             -1, MV_BIT_RIGHT
8692   },
8693   {
8694     Xpush_nut_w,                        FALSE,  FALSE,
8695     EL_NUT,                             -1, MV_BIT_LEFT
8696   },
8697   {
8698     Xpush_spring_e,                     FALSE,  FALSE,
8699     EL_SPRING_RIGHT,                    -1, MV_BIT_RIGHT
8700   },
8701   {
8702     Xpush_spring_w,                     FALSE,  FALSE,
8703     EL_SPRING_LEFT,                     -1, MV_BIT_LEFT
8704   },
8705
8706   {
8707     Xdynamite,                          TRUE,   FALSE,
8708     EL_EM_DYNAMITE,                     -1, -1
8709   },
8710   {
8711     Ydynamite_blank,                    FALSE,  FALSE,
8712     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
8713   },
8714   {
8715     Xdynamite_1,                        TRUE,   FALSE,
8716     EL_EM_DYNAMITE_ACTIVE,              -1, -1
8717   },
8718   {
8719     Xdynamite_2,                        FALSE,  FALSE,
8720     EL_EM_DYNAMITE_ACTIVE,              -1, -1
8721   },
8722   {
8723     Xdynamite_3,                        FALSE,  FALSE,
8724     EL_EM_DYNAMITE_ACTIVE,              -1, -1
8725   },
8726   {
8727     Xdynamite_4,                        FALSE,  FALSE,
8728     EL_EM_DYNAMITE_ACTIVE,              -1, -1
8729   },
8730
8731   {
8732     Xkey_1,                             TRUE,   FALSE,
8733     EL_EM_KEY_1,                        -1, -1
8734   },
8735   {
8736     Xkey_2,                             TRUE,   FALSE,
8737     EL_EM_KEY_2,                        -1, -1
8738   },
8739   {
8740     Xkey_3,                             TRUE,   FALSE,
8741     EL_EM_KEY_3,                        -1, -1
8742   },
8743   {
8744     Xkey_4,                             TRUE,   FALSE,
8745     EL_EM_KEY_4,                        -1, -1
8746   },
8747   {
8748     Xkey_5,                             TRUE,   FALSE,
8749     EL_EMC_KEY_5,                       -1, -1
8750   },
8751   {
8752     Xkey_6,                             TRUE,   FALSE,
8753     EL_EMC_KEY_6,                       -1, -1
8754   },
8755   {
8756     Xkey_7,                             TRUE,   FALSE,
8757     EL_EMC_KEY_7,                       -1, -1
8758   },
8759   {
8760     Xkey_8,                             TRUE,   FALSE,
8761     EL_EMC_KEY_8,                       -1, -1
8762   },
8763
8764   {
8765     Xdoor_1,                            TRUE,   FALSE,
8766     EL_EM_GATE_1,                       -1, -1
8767   },
8768   {
8769     Xdoor_2,                            TRUE,   FALSE,
8770     EL_EM_GATE_2,                       -1, -1
8771   },
8772   {
8773     Xdoor_3,                            TRUE,   FALSE,
8774     EL_EM_GATE_3,                       -1, -1
8775   },
8776   {
8777     Xdoor_4,                            TRUE,   FALSE,
8778     EL_EM_GATE_4,                       -1, -1
8779   },
8780   {
8781     Xdoor_5,                            TRUE,   FALSE,
8782     EL_EMC_GATE_5,                      -1, -1
8783   },
8784   {
8785     Xdoor_6,                            TRUE,   FALSE,
8786     EL_EMC_GATE_6,                      -1, -1
8787   },
8788   {
8789     Xdoor_7,                            TRUE,   FALSE,
8790     EL_EMC_GATE_7,                      -1, -1
8791   },
8792   {
8793     Xdoor_8,                            TRUE,   FALSE,
8794     EL_EMC_GATE_8,                      -1, -1
8795   },
8796
8797   {
8798     Xfake_door_1,                       TRUE,   FALSE,
8799     EL_EM_GATE_1_GRAY,                  -1, -1
8800   },
8801   {
8802     Xfake_door_2,                       TRUE,   FALSE,
8803     EL_EM_GATE_2_GRAY,                  -1, -1
8804   },
8805   {
8806     Xfake_door_3,                       TRUE,   FALSE,
8807     EL_EM_GATE_3_GRAY,                  -1, -1
8808   },
8809   {
8810     Xfake_door_4,                       TRUE,   FALSE,
8811     EL_EM_GATE_4_GRAY,                  -1, -1
8812   },
8813   {
8814     Xfake_door_5,                       TRUE,   FALSE,
8815     EL_EMC_GATE_5_GRAY,                 -1, -1
8816   },
8817   {
8818     Xfake_door_6,                       TRUE,   FALSE,
8819     EL_EMC_GATE_6_GRAY,                 -1, -1
8820   },
8821   {
8822     Xfake_door_7,                       TRUE,   FALSE,
8823     EL_EMC_GATE_7_GRAY,                 -1, -1
8824   },
8825   {
8826     Xfake_door_8,                       TRUE,   FALSE,
8827     EL_EMC_GATE_8_GRAY,                 -1, -1
8828   },
8829
8830   {
8831     Xballoon,                           TRUE,   FALSE,
8832     EL_BALLOON,                         -1, -1
8833   },
8834   {
8835     Yballoon_n,                         FALSE,  FALSE,
8836     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
8837   },
8838   {
8839     Yballoon_nB,                        FALSE,  TRUE,
8840     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
8841   },
8842   {
8843     Yballoon_e,                         FALSE,  FALSE,
8844     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
8845   },
8846   {
8847     Yballoon_eB,                        FALSE,  TRUE,
8848     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
8849   },
8850   {
8851     Yballoon_s,                         FALSE,  FALSE,
8852     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
8853   },
8854   {
8855     Yballoon_sB,                        FALSE,  TRUE,
8856     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
8857   },
8858   {
8859     Yballoon_w,                         FALSE,  FALSE,
8860     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
8861   },
8862   {
8863     Yballoon_wB,                        FALSE,  TRUE,
8864     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
8865   },
8866
8867   {
8868     Xball_1,                            TRUE,   FALSE,
8869     EL_EMC_MAGIC_BALL,                  -1, -1
8870   },
8871   {
8872     Yball_1,                            FALSE,  FALSE,
8873     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
8874   },
8875   {
8876     Xball_2,                            FALSE,  FALSE,
8877     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
8878   },
8879   {
8880     Yball_2,                            FALSE,  FALSE,
8881     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
8882   },
8883   {
8884     Yball_blank,                        FALSE,  FALSE,
8885     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
8886   },
8887
8888   {
8889     Xamoeba_1,                          TRUE,   FALSE,
8890     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
8891   },
8892   {
8893     Xamoeba_2,                          FALSE,  FALSE,
8894     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
8895   },
8896   {
8897     Xamoeba_3,                          FALSE,  FALSE,
8898     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
8899   },
8900   {
8901     Xamoeba_4,                          FALSE,  FALSE,
8902     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
8903   },
8904   {
8905     Xamoeba_5,                          TRUE,   FALSE,
8906     EL_AMOEBA_WET,                      ACTION_OTHER, -1
8907   },
8908   {
8909     Xamoeba_6,                          FALSE,  FALSE,
8910     EL_AMOEBA_WET,                      ACTION_OTHER, -1
8911   },
8912   {
8913     Xamoeba_7,                          FALSE,  FALSE,
8914     EL_AMOEBA_WET,                      ACTION_OTHER, -1
8915   },
8916   {
8917     Xamoeba_8,                          FALSE,  FALSE,
8918     EL_AMOEBA_WET,                      ACTION_OTHER, -1
8919   },
8920
8921   {
8922     Xdrip,                              TRUE,   FALSE,
8923     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
8924   },
8925   {
8926     Xdrip_fall,                         FALSE,  FALSE,
8927     EL_AMOEBA_DROP,                     -1, -1
8928   },
8929   {
8930     Xdrip_stretch,                      FALSE,  FALSE,
8931     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8932   },
8933   {
8934     Xdrip_stretchB,                     FALSE,  TRUE,
8935     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8936   },
8937   {
8938     Ydrip_1_s,                          FALSE,  FALSE,
8939     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8940   },
8941   {
8942     Ydrip_1_sB,                         FALSE,  TRUE,
8943     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8944   },
8945   {
8946     Ydrip_2_s,                          FALSE,  FALSE,
8947     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8948   },
8949   {
8950     Ydrip_2_sB,                         FALSE,  TRUE,
8951     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8952   },
8953
8954   {
8955     Xwonderwall,                        TRUE,   FALSE,
8956     EL_MAGIC_WALL,                      -1, -1
8957   },
8958   {
8959     Ywonderwall,                        FALSE,  FALSE,
8960     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
8961   },
8962
8963   {
8964     Xwheel,                             TRUE,   FALSE,
8965     EL_ROBOT_WHEEL,                     -1, -1
8966   },
8967   {
8968     Ywheel,                             FALSE,  FALSE,
8969     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
8970   },
8971
8972   {
8973     Xswitch,                            TRUE,   FALSE,
8974     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
8975   },
8976   {
8977     Yswitch,                            FALSE,  FALSE,
8978     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
8979   },
8980
8981   {
8982     Xbumper,                            TRUE,   FALSE,
8983     EL_EMC_SPRING_BUMPER,               -1, -1
8984   },
8985   {
8986     Ybumper,                            FALSE,  FALSE,
8987     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
8988   },
8989
8990   {
8991     Xacid_nw,                           TRUE,   FALSE,
8992     EL_ACID_POOL_TOPLEFT,               -1, -1
8993   },
8994   {
8995     Xacid_ne,                           TRUE,   FALSE,
8996     EL_ACID_POOL_TOPRIGHT,              -1, -1
8997   },
8998   {
8999     Xacid_sw,                           TRUE,   FALSE,
9000     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
9001   },
9002   {
9003     Xacid_s,                            TRUE,   FALSE,
9004     EL_ACID_POOL_BOTTOM,                -1, -1
9005   },
9006   {
9007     Xacid_se,                           TRUE,   FALSE,
9008     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
9009   },
9010
9011   {
9012     Xfake_blank,                        TRUE,   FALSE,
9013     EL_INVISIBLE_WALL,                  -1, -1
9014   },
9015   {
9016     Yfake_blank,                        FALSE,  FALSE,
9017     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
9018   },
9019
9020   {
9021     Xfake_grass,                        TRUE,   FALSE,
9022     EL_EMC_FAKE_GRASS,                  -1, -1
9023   },
9024   {
9025     Yfake_grass,                        FALSE,  FALSE,
9026     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
9027   },
9028
9029   {
9030     Xfake_amoeba,                       TRUE,   FALSE,
9031     EL_EMC_DRIPPER,                     -1, -1
9032   },
9033   {
9034     Yfake_amoeba,                       FALSE,  FALSE,
9035     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
9036   },
9037
9038   {
9039     Xlenses,                            TRUE,   FALSE,
9040     EL_EMC_LENSES,                      -1, -1
9041   },
9042
9043   {
9044     Xmagnify,                           TRUE,   FALSE,
9045     EL_EMC_MAGNIFIER,                   -1, -1
9046   },
9047
9048   {
9049     Xsand,                              TRUE,   FALSE,
9050     EL_QUICKSAND_EMPTY,                 -1, -1
9051   },
9052   {
9053     Xsand_stone,                        TRUE,   FALSE,
9054     EL_QUICKSAND_FULL,                  -1, -1
9055   },
9056   {
9057     Xsand_stonein_1,                    FALSE,  TRUE,
9058     EL_ROCK,                            ACTION_FILLING, -1
9059   },
9060   {
9061     Xsand_stonein_2,                    FALSE,  TRUE,
9062     EL_ROCK,                            ACTION_FILLING, -1
9063   },
9064   {
9065     Xsand_stonein_3,                    FALSE,  TRUE,
9066     EL_ROCK,                            ACTION_FILLING, -1
9067   },
9068   {
9069     Xsand_stonein_4,                    FALSE,  TRUE,
9070     EL_ROCK,                            ACTION_FILLING, -1
9071   },
9072   {
9073     Xsand_sandstone_1,                  FALSE,  FALSE,
9074     EL_QUICKSAND_FILLING,               -1, -1
9075   },
9076   {
9077     Xsand_sandstone_2,                  FALSE,  FALSE,
9078     EL_QUICKSAND_FILLING,               -1, -1
9079   },
9080   {
9081     Xsand_sandstone_3,                  FALSE,  FALSE,
9082     EL_QUICKSAND_FILLING,               -1, -1
9083   },
9084   {
9085     Xsand_sandstone_4,                  FALSE,  FALSE,
9086     EL_QUICKSAND_FILLING,               -1, -1
9087   },
9088   {
9089     Xsand_stonesand_1,                  FALSE,  FALSE,
9090     EL_QUICKSAND_EMPTYING,              -1, -1
9091   },
9092   {
9093     Xsand_stonesand_2,                  FALSE,  FALSE,
9094     EL_QUICKSAND_EMPTYING,              -1, -1
9095   },
9096   {
9097     Xsand_stonesand_3,                  FALSE,  FALSE,
9098     EL_QUICKSAND_EMPTYING,              -1, -1
9099   },
9100   {
9101     Xsand_stonesand_4,                  FALSE,  FALSE,
9102     EL_QUICKSAND_EMPTYING,              -1, -1
9103   },
9104   {
9105     Xsand_stoneout_1,                   FALSE,  FALSE,
9106     EL_ROCK,                            ACTION_EMPTYING, -1
9107   },
9108   {
9109     Xsand_stoneout_2,                   FALSE,  FALSE,
9110     EL_ROCK,                            ACTION_EMPTYING, -1
9111   },
9112   {
9113     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
9114     EL_QUICKSAND_EMPTYING,              -1, -1
9115   },
9116   {
9117     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
9118     EL_QUICKSAND_EMPTYING,              -1, -1
9119   },
9120
9121   {
9122     Xslide_ns,                          TRUE,   FALSE,
9123     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
9124   },
9125   {
9126     Yslide_ns_blank,                    FALSE,  FALSE,
9127     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
9128   },
9129   {
9130     Xslide_ew,                          TRUE,   FALSE,
9131     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
9132   },
9133   {
9134     Yslide_ew_blank,                    FALSE,  FALSE,
9135     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
9136   },
9137
9138   {
9139     Xwind_n,                            TRUE,   FALSE,
9140     EL_BALLOON_SWITCH_UP,               -1, -1
9141   },
9142   {
9143     Xwind_e,                            TRUE,   FALSE,
9144     EL_BALLOON_SWITCH_RIGHT,            -1, -1
9145   },
9146   {
9147     Xwind_s,                            TRUE,   FALSE,
9148     EL_BALLOON_SWITCH_DOWN,             -1, -1
9149   },
9150   {
9151     Xwind_w,                            TRUE,   FALSE,
9152     EL_BALLOON_SWITCH_LEFT,             -1, -1
9153   },
9154   {
9155     Xwind_any,                          TRUE,   FALSE,
9156     EL_BALLOON_SWITCH_ANY,              -1, -1
9157   },
9158   {
9159     Xwind_stop,                         TRUE,   FALSE,
9160     EL_BALLOON_SWITCH_NONE,             -1, -1
9161   },
9162
9163   {
9164     Xexit,                              TRUE,   FALSE,
9165     EL_EM_EXIT_CLOSED,                  -1, -1
9166   },
9167   {
9168     Xexit_1,                            TRUE,   FALSE,
9169     EL_EM_EXIT_OPEN,                    -1, -1
9170   },
9171   {
9172     Xexit_2,                            FALSE,  FALSE,
9173     EL_EM_EXIT_OPEN,                    -1, -1
9174   },
9175   {
9176     Xexit_3,                            FALSE,  FALSE,
9177     EL_EM_EXIT_OPEN,                    -1, -1
9178   },
9179
9180   {
9181     Xpause,                             FALSE,  FALSE,
9182     EL_EMPTY,                           -1, -1
9183   },
9184
9185   {
9186     Xwall_1,                            TRUE,   FALSE,
9187     EL_WALL,                            -1, -1
9188   },
9189   {
9190     Xwall_2,                            TRUE,   FALSE,
9191     EL_EMC_WALL_14,                     -1, -1
9192   },
9193   {
9194     Xwall_3,                            TRUE,   FALSE,
9195     EL_EMC_WALL_15,                     -1, -1
9196   },
9197   {
9198     Xwall_4,                            TRUE,   FALSE,
9199     EL_EMC_WALL_16,                     -1, -1
9200   },
9201
9202   {
9203     Xroundwall_1,                       TRUE,   FALSE,
9204     EL_WALL_SLIPPERY,                   -1, -1
9205   },
9206   {
9207     Xroundwall_2,                       TRUE,   FALSE,
9208     EL_EMC_WALL_SLIPPERY_2,             -1, -1
9209   },
9210   {
9211     Xroundwall_3,                       TRUE,   FALSE,
9212     EL_EMC_WALL_SLIPPERY_3,             -1, -1
9213   },
9214   {
9215     Xroundwall_4,                       TRUE,   FALSE,
9216     EL_EMC_WALL_SLIPPERY_4,             -1, -1
9217   },
9218
9219   {
9220     Xsteel_1,                           TRUE,   FALSE,
9221     EL_STEELWALL,                       -1, -1
9222   },
9223   {
9224     Xsteel_2,                           TRUE,   FALSE,
9225     EL_EMC_STEELWALL_2,                 -1, -1
9226   },
9227   {
9228     Xsteel_3,                           TRUE,   FALSE,
9229     EL_EMC_STEELWALL_3,                 -1, -1
9230   },
9231   {
9232     Xsteel_4,                           TRUE,   FALSE,
9233     EL_EMC_STEELWALL_4,                 -1, -1
9234   },
9235
9236   {
9237     Xdecor_1,                           TRUE,   FALSE,
9238     EL_EMC_WALL_8,                      -1, -1
9239   },
9240   {
9241     Xdecor_2,                           TRUE,   FALSE,
9242     EL_EMC_WALL_6,                      -1, -1
9243   },
9244   {
9245     Xdecor_3,                           TRUE,   FALSE,
9246     EL_EMC_WALL_4,                      -1, -1
9247   },
9248   {
9249     Xdecor_4,                           TRUE,   FALSE,
9250     EL_EMC_WALL_7,                      -1, -1
9251   },
9252   {
9253     Xdecor_5,                           TRUE,   FALSE,
9254     EL_EMC_WALL_5,                      -1, -1
9255   },
9256   {
9257     Xdecor_6,                           TRUE,   FALSE,
9258     EL_EMC_WALL_9,                      -1, -1
9259   },
9260   {
9261     Xdecor_7,                           TRUE,   FALSE,
9262     EL_EMC_WALL_10,                     -1, -1
9263   },
9264   {
9265     Xdecor_8,                           TRUE,   FALSE,
9266     EL_EMC_WALL_1,                      -1, -1
9267   },
9268   {
9269     Xdecor_9,                           TRUE,   FALSE,
9270     EL_EMC_WALL_2,                      -1, -1
9271   },
9272   {
9273     Xdecor_10,                          TRUE,   FALSE,
9274     EL_EMC_WALL_3,                      -1, -1
9275   },
9276   {
9277     Xdecor_11,                          TRUE,   FALSE,
9278     EL_EMC_WALL_11,                     -1, -1
9279   },
9280   {
9281     Xdecor_12,                          TRUE,   FALSE,
9282     EL_EMC_WALL_12,                     -1, -1
9283   },
9284
9285   {
9286     Xalpha_0,                           TRUE,   FALSE,
9287     EL_CHAR('0'),                       -1, -1
9288   },
9289   {
9290     Xalpha_1,                           TRUE,   FALSE,
9291     EL_CHAR('1'),                       -1, -1
9292   },
9293   {
9294     Xalpha_2,                           TRUE,   FALSE,
9295     EL_CHAR('2'),                       -1, -1
9296   },
9297   {
9298     Xalpha_3,                           TRUE,   FALSE,
9299     EL_CHAR('3'),                       -1, -1
9300   },
9301   {
9302     Xalpha_4,                           TRUE,   FALSE,
9303     EL_CHAR('4'),                       -1, -1
9304   },
9305   {
9306     Xalpha_5,                           TRUE,   FALSE,
9307     EL_CHAR('5'),                       -1, -1
9308   },
9309   {
9310     Xalpha_6,                           TRUE,   FALSE,
9311     EL_CHAR('6'),                       -1, -1
9312   },
9313   {
9314     Xalpha_7,                           TRUE,   FALSE,
9315     EL_CHAR('7'),                       -1, -1
9316   },
9317   {
9318     Xalpha_8,                           TRUE,   FALSE,
9319     EL_CHAR('8'),                       -1, -1
9320   },
9321   {
9322     Xalpha_9,                           TRUE,   FALSE,
9323     EL_CHAR('9'),                       -1, -1
9324   },
9325   {
9326     Xalpha_excla,                       TRUE,   FALSE,
9327     EL_CHAR('!'),                       -1, -1
9328   },
9329   {
9330     Xalpha_apost,                       TRUE,   FALSE,
9331     EL_CHAR('\''),                      -1, -1
9332   },
9333   {
9334     Xalpha_comma,                       TRUE,   FALSE,
9335     EL_CHAR(','),                       -1, -1
9336   },
9337   {
9338     Xalpha_minus,                       TRUE,   FALSE,
9339     EL_CHAR('-'),                       -1, -1
9340   },
9341   {
9342     Xalpha_perio,                       TRUE,   FALSE,
9343     EL_CHAR('.'),                       -1, -1
9344   },
9345   {
9346     Xalpha_colon,                       TRUE,   FALSE,
9347     EL_CHAR(':'),                       -1, -1
9348   },
9349   {
9350     Xalpha_quest,                       TRUE,   FALSE,
9351     EL_CHAR('?'),                       -1, -1
9352   },
9353   {
9354     Xalpha_a,                           TRUE,   FALSE,
9355     EL_CHAR('A'),                       -1, -1
9356   },
9357   {
9358     Xalpha_b,                           TRUE,   FALSE,
9359     EL_CHAR('B'),                       -1, -1
9360   },
9361   {
9362     Xalpha_c,                           TRUE,   FALSE,
9363     EL_CHAR('C'),                       -1, -1
9364   },
9365   {
9366     Xalpha_d,                           TRUE,   FALSE,
9367     EL_CHAR('D'),                       -1, -1
9368   },
9369   {
9370     Xalpha_e,                           TRUE,   FALSE,
9371     EL_CHAR('E'),                       -1, -1
9372   },
9373   {
9374     Xalpha_f,                           TRUE,   FALSE,
9375     EL_CHAR('F'),                       -1, -1
9376   },
9377   {
9378     Xalpha_g,                           TRUE,   FALSE,
9379     EL_CHAR('G'),                       -1, -1
9380   },
9381   {
9382     Xalpha_h,                           TRUE,   FALSE,
9383     EL_CHAR('H'),                       -1, -1
9384   },
9385   {
9386     Xalpha_i,                           TRUE,   FALSE,
9387     EL_CHAR('I'),                       -1, -1
9388   },
9389   {
9390     Xalpha_j,                           TRUE,   FALSE,
9391     EL_CHAR('J'),                       -1, -1
9392   },
9393   {
9394     Xalpha_k,                           TRUE,   FALSE,
9395     EL_CHAR('K'),                       -1, -1
9396   },
9397   {
9398     Xalpha_l,                           TRUE,   FALSE,
9399     EL_CHAR('L'),                       -1, -1
9400   },
9401   {
9402     Xalpha_m,                           TRUE,   FALSE,
9403     EL_CHAR('M'),                       -1, -1
9404   },
9405   {
9406     Xalpha_n,                           TRUE,   FALSE,
9407     EL_CHAR('N'),                       -1, -1
9408   },
9409   {
9410     Xalpha_o,                           TRUE,   FALSE,
9411     EL_CHAR('O'),                       -1, -1
9412   },
9413   {
9414     Xalpha_p,                           TRUE,   FALSE,
9415     EL_CHAR('P'),                       -1, -1
9416   },
9417   {
9418     Xalpha_q,                           TRUE,   FALSE,
9419     EL_CHAR('Q'),                       -1, -1
9420   },
9421   {
9422     Xalpha_r,                           TRUE,   FALSE,
9423     EL_CHAR('R'),                       -1, -1
9424   },
9425   {
9426     Xalpha_s,                           TRUE,   FALSE,
9427     EL_CHAR('S'),                       -1, -1
9428   },
9429   {
9430     Xalpha_t,                           TRUE,   FALSE,
9431     EL_CHAR('T'),                       -1, -1
9432   },
9433   {
9434     Xalpha_u,                           TRUE,   FALSE,
9435     EL_CHAR('U'),                       -1, -1
9436   },
9437   {
9438     Xalpha_v,                           TRUE,   FALSE,
9439     EL_CHAR('V'),                       -1, -1
9440   },
9441   {
9442     Xalpha_w,                           TRUE,   FALSE,
9443     EL_CHAR('W'),                       -1, -1
9444   },
9445   {
9446     Xalpha_x,                           TRUE,   FALSE,
9447     EL_CHAR('X'),                       -1, -1
9448   },
9449   {
9450     Xalpha_y,                           TRUE,   FALSE,
9451     EL_CHAR('Y'),                       -1, -1
9452   },
9453   {
9454     Xalpha_z,                           TRUE,   FALSE,
9455     EL_CHAR('Z'),                       -1, -1
9456   },
9457   {
9458     Xalpha_arrow_e,                     TRUE,   FALSE,
9459     EL_CHAR('>'),                       -1, -1
9460   },
9461   {
9462     Xalpha_arrow_w,                     TRUE,   FALSE,
9463     EL_CHAR('<'),                       -1, -1
9464   },
9465   {
9466     Xalpha_copyr,                       TRUE,   FALSE,
9467     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
9468   },
9469
9470   {
9471     Ykey_1_blank,                       FALSE,  FALSE,
9472     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
9473   },
9474   {
9475     Ykey_2_blank,                       FALSE,  FALSE,
9476     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
9477   },
9478   {
9479     Ykey_3_blank,                       FALSE,  FALSE,
9480     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
9481   },
9482   {
9483     Ykey_4_blank,                       FALSE,  FALSE,
9484     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
9485   },
9486   {
9487     Ykey_5_blank,                       FALSE,  FALSE,
9488     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
9489   },
9490   {
9491     Ykey_6_blank,                       FALSE,  FALSE,
9492     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
9493   },
9494   {
9495     Ykey_7_blank,                       FALSE,  FALSE,
9496     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
9497   },
9498   {
9499     Ykey_8_blank,                       FALSE,  FALSE,
9500     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
9501   },
9502   {
9503     Ylenses_blank,                      FALSE,  FALSE,
9504     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
9505   },
9506   {
9507     Ymagnify_blank,                     FALSE,  FALSE,
9508     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
9509   },
9510   {
9511     Ygrass_blank,                       FALSE,  FALSE,
9512     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
9513   },
9514   {
9515     Ydirt_blank,                        FALSE,  FALSE,
9516     EL_SAND,                            ACTION_SNAPPING, -1
9517   },
9518
9519   {
9520     -1,                                 FALSE,  FALSE,
9521     -1,                                 -1, -1
9522   }
9523 };
9524
9525 static struct Mapping_EM_to_RND_player
9526 {
9527   int action_em;
9528   int player_nr;
9529
9530   int element_rnd;
9531   int action;
9532   int direction;
9533 }
9534 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
9535 {
9536   {
9537     PLY_walk_n,                         0,
9538     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
9539   },
9540   {
9541     PLY_walk_e,                         0,
9542     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
9543   },
9544   {
9545     PLY_walk_s,                         0,
9546     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
9547   },
9548   {
9549     PLY_walk_w,                         0,
9550     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
9551   },
9552   {
9553     PLY_push_n,                         0,
9554     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
9555   },
9556   {
9557     PLY_push_e,                         0,
9558     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
9559   },
9560   {
9561     PLY_push_s,                         0,
9562     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
9563   },
9564   {
9565     PLY_push_w,                         0,
9566     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
9567   },
9568   {
9569     PLY_shoot_n,                        0,
9570     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
9571   },
9572   {
9573     PLY_shoot_e,                        0,
9574     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
9575   },
9576   {
9577     PLY_shoot_s,                        0,
9578     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
9579   },
9580   {
9581     PLY_shoot_w,                        0,
9582     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
9583   },
9584   {
9585     PLY_walk_n,                         1,
9586     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
9587   },
9588   {
9589     PLY_walk_e,                         1,
9590     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
9591   },
9592   {
9593     PLY_walk_s,                         1,
9594     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
9595   },
9596   {
9597     PLY_walk_w,                         1,
9598     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
9599   },
9600   {
9601     PLY_push_n,                         1,
9602     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
9603   },
9604   {
9605     PLY_push_e,                         1,
9606     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
9607   },
9608   {
9609     PLY_push_s,                         1,
9610     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
9611   },
9612   {
9613     PLY_push_w,                         1,
9614     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
9615   },
9616   {
9617     PLY_shoot_n,                        1,
9618     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
9619   },
9620   {
9621     PLY_shoot_e,                        1,
9622     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
9623   },
9624   {
9625     PLY_shoot_s,                        1,
9626     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
9627   },
9628   {
9629     PLY_shoot_w,                        1,
9630     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
9631   },
9632   {
9633     PLY_still,                          0,
9634     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
9635   },
9636   {
9637     PLY_still,                          1,
9638     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
9639   },
9640   {
9641     PLY_walk_n,                         2,
9642     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
9643   },
9644   {
9645     PLY_walk_e,                         2,
9646     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
9647   },
9648   {
9649     PLY_walk_s,                         2,
9650     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
9651   },
9652   {
9653     PLY_walk_w,                         2,
9654     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
9655   },
9656   {
9657     PLY_push_n,                         2,
9658     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
9659   },
9660   {
9661     PLY_push_e,                         2,
9662     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
9663   },
9664   {
9665     PLY_push_s,                         2,
9666     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
9667   },
9668   {
9669     PLY_push_w,                         2,
9670     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
9671   },
9672   {
9673     PLY_shoot_n,                        2,
9674     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
9675   },
9676   {
9677     PLY_shoot_e,                        2,
9678     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
9679   },
9680   {
9681     PLY_shoot_s,                        2,
9682     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
9683   },
9684   {
9685     PLY_shoot_w,                        2,
9686     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
9687   },
9688   {
9689     PLY_walk_n,                         3,
9690     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
9691   },
9692   {
9693     PLY_walk_e,                         3,
9694     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
9695   },
9696   {
9697     PLY_walk_s,                         3,
9698     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
9699   },
9700   {
9701     PLY_walk_w,                         3,
9702     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
9703   },
9704   {
9705     PLY_push_n,                         3,
9706     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
9707   },
9708   {
9709     PLY_push_e,                         3,
9710     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
9711   },
9712   {
9713     PLY_push_s,                         3,
9714     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
9715   },
9716   {
9717     PLY_push_w,                         3,
9718     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
9719   },
9720   {
9721     PLY_shoot_n,                        3,
9722     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
9723   },
9724   {
9725     PLY_shoot_e,                        3,
9726     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
9727   },
9728   {
9729     PLY_shoot_s,                        3,
9730     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
9731   },
9732   {
9733     PLY_shoot_w,                        3,
9734     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
9735   },
9736   {
9737     PLY_still,                          2,
9738     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
9739   },
9740   {
9741     PLY_still,                          3,
9742     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
9743   },
9744
9745   {
9746     -1,                                 -1,
9747     -1,                                 -1, -1
9748   }
9749 };
9750
9751 int map_element_RND_to_EM_cave(int element_rnd)
9752 {
9753   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
9754   static boolean mapping_initialized = FALSE;
9755
9756   if (!mapping_initialized)
9757   {
9758     int i;
9759
9760     // return "Xalpha_quest" for all undefined elements in mapping array
9761     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9762       mapping_RND_to_EM[i] = Xalpha_quest;
9763
9764     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9765       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
9766         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
9767           em_object_mapping_list[i].element_em;
9768
9769     mapping_initialized = TRUE;
9770   }
9771
9772   if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
9773   {
9774     Warn("invalid RND level element %d", element_rnd);
9775
9776     return EL_UNKNOWN;
9777   }
9778
9779   return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
9780 }
9781
9782 int map_element_EM_to_RND_cave(int element_em_cave)
9783 {
9784   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9785   static boolean mapping_initialized = FALSE;
9786
9787   if (!mapping_initialized)
9788   {
9789     int i;
9790
9791     // return "EL_UNKNOWN" for all undefined elements in mapping array
9792     for (i = 0; i < GAME_TILE_MAX; i++)
9793       mapping_EM_to_RND[i] = EL_UNKNOWN;
9794
9795     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9796       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9797         em_object_mapping_list[i].element_rnd;
9798
9799     mapping_initialized = TRUE;
9800   }
9801
9802   if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
9803   {
9804     Warn("invalid EM cave element %d", element_em_cave);
9805
9806     return EL_UNKNOWN;
9807   }
9808
9809   return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
9810 }
9811
9812 int map_element_EM_to_RND_game(int element_em_game)
9813 {
9814   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9815   static boolean mapping_initialized = FALSE;
9816
9817   if (!mapping_initialized)
9818   {
9819     int i;
9820
9821     // return "EL_UNKNOWN" for all undefined elements in mapping array
9822     for (i = 0; i < GAME_TILE_MAX; i++)
9823       mapping_EM_to_RND[i] = EL_UNKNOWN;
9824
9825     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9826       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9827         em_object_mapping_list[i].element_rnd;
9828
9829     mapping_initialized = TRUE;
9830   }
9831
9832   if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
9833   {
9834     Warn("invalid EM game element %d", element_em_game);
9835
9836     return EL_UNKNOWN;
9837   }
9838
9839   return mapping_EM_to_RND[element_em_game];
9840 }
9841
9842 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
9843 {
9844   struct LevelInfo_EM *level_em = level->native_em_level;
9845   struct CAVE *cav = level_em->cav;
9846   int i, j;
9847
9848   for (i = 0; i < GAME_TILE_MAX; i++)
9849     cav->android_array[i] = Cblank;
9850
9851   for (i = 0; i < level->num_android_clone_elements; i++)
9852   {
9853     int element_rnd = level->android_clone_element[i];
9854     int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
9855
9856     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
9857       if (em_object_mapping_list[j].element_rnd == element_rnd)
9858         cav->android_array[em_object_mapping_list[j].element_em] =
9859           element_em_cave;
9860   }
9861 }
9862
9863 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
9864 {
9865   struct LevelInfo_EM *level_em = level->native_em_level;
9866   struct CAVE *cav = level_em->cav;
9867   int i, j;
9868
9869   level->num_android_clone_elements = 0;
9870
9871   for (i = 0; i < GAME_TILE_MAX; i++)
9872   {
9873     int element_em_cave = cav->android_array[i];
9874     int element_rnd;
9875     boolean element_found = FALSE;
9876
9877     if (element_em_cave == Cblank)
9878       continue;
9879
9880     element_rnd = map_element_EM_to_RND_cave(element_em_cave);
9881
9882     for (j = 0; j < level->num_android_clone_elements; j++)
9883       if (level->android_clone_element[j] == element_rnd)
9884         element_found = TRUE;
9885
9886     if (!element_found)
9887     {
9888       level->android_clone_element[level->num_android_clone_elements++] =
9889         element_rnd;
9890
9891       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
9892         break;
9893     }
9894   }
9895
9896   if (level->num_android_clone_elements == 0)
9897   {
9898     level->num_android_clone_elements = 1;
9899     level->android_clone_element[0] = EL_EMPTY;
9900   }
9901 }
9902
9903 int map_direction_RND_to_EM(int direction)
9904 {
9905   return (direction == MV_UP    ? 0 :
9906           direction == MV_RIGHT ? 1 :
9907           direction == MV_DOWN  ? 2 :
9908           direction == MV_LEFT  ? 3 :
9909           -1);
9910 }
9911
9912 int map_direction_EM_to_RND(int direction)
9913 {
9914   return (direction == 0 ? MV_UP    :
9915           direction == 1 ? MV_RIGHT :
9916           direction == 2 ? MV_DOWN  :
9917           direction == 3 ? MV_LEFT  :
9918           MV_NONE);
9919 }
9920
9921 int map_element_RND_to_SP(int element_rnd)
9922 {
9923   int element_sp = 0x20;        // map unknown elements to yellow "hardware"
9924
9925   if (element_rnd >= EL_SP_START &&
9926       element_rnd <= EL_SP_END)
9927     element_sp = element_rnd - EL_SP_START;
9928   else if (element_rnd == EL_EMPTY_SPACE)
9929     element_sp = 0x00;
9930   else if (element_rnd == EL_INVISIBLE_WALL)
9931     element_sp = 0x28;
9932
9933   return element_sp;
9934 }
9935
9936 int map_element_SP_to_RND(int element_sp)
9937 {
9938   int element_rnd = EL_UNKNOWN;
9939
9940   if (element_sp >= 0x00 &&
9941       element_sp <= 0x27)
9942     element_rnd = EL_SP_START + element_sp;
9943   else if (element_sp == 0x28)
9944     element_rnd = EL_INVISIBLE_WALL;
9945
9946   return element_rnd;
9947 }
9948
9949 int map_action_SP_to_RND(int action_sp)
9950 {
9951   switch (action_sp)
9952   {
9953     case actActive:             return ACTION_ACTIVE;
9954     case actImpact:             return ACTION_IMPACT;
9955     case actExploding:          return ACTION_EXPLODING;
9956     case actDigging:            return ACTION_DIGGING;
9957     case actSnapping:           return ACTION_SNAPPING;
9958     case actCollecting:         return ACTION_COLLECTING;
9959     case actPassing:            return ACTION_PASSING;
9960     case actPushing:            return ACTION_PUSHING;
9961     case actDropping:           return ACTION_DROPPING;
9962
9963     default:                    return ACTION_DEFAULT;
9964   }
9965 }
9966
9967 int map_element_RND_to_MM(int element_rnd)
9968 {
9969   return (element_rnd >= EL_MM_START_1 &&
9970           element_rnd <= EL_MM_END_1 ?
9971           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
9972
9973           element_rnd >= EL_MM_START_2 &&
9974           element_rnd <= EL_MM_END_2 ?
9975           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
9976
9977           element_rnd >= EL_MM_START_3 &&
9978           element_rnd <= EL_MM_END_3 ?
9979           EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
9980
9981           element_rnd >= EL_CHAR_START &&
9982           element_rnd <= EL_CHAR_END ?
9983           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
9984
9985           element_rnd >= EL_MM_RUNTIME_START &&
9986           element_rnd <= EL_MM_RUNTIME_END ?
9987           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
9988
9989           EL_MM_EMPTY_NATIVE);
9990 }
9991
9992 int map_element_MM_to_RND(int element_mm)
9993 {
9994   return (element_mm == EL_MM_EMPTY_NATIVE ||
9995           element_mm == EL_DF_EMPTY_NATIVE ?
9996           EL_EMPTY :
9997
9998           element_mm >= EL_MM_START_1_NATIVE &&
9999           element_mm <= EL_MM_END_1_NATIVE ?
10000           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
10001
10002           element_mm >= EL_MM_START_2_NATIVE &&
10003           element_mm <= EL_MM_END_2_NATIVE ?
10004           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
10005
10006           element_mm >= EL_MM_START_3_NATIVE &&
10007           element_mm <= EL_MM_END_3_NATIVE ?
10008           EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
10009
10010           element_mm >= EL_MM_CHAR_START_NATIVE &&
10011           element_mm <= EL_MM_CHAR_END_NATIVE ?
10012           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
10013
10014           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
10015           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
10016           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
10017
10018           EL_EMPTY);
10019 }
10020
10021 int map_action_MM_to_RND(int action_mm)
10022 {
10023   // all MM actions are defined to exactly match their RND counterparts
10024   return action_mm;
10025 }
10026
10027 int map_sound_MM_to_RND(int sound_mm)
10028 {
10029   switch (sound_mm)
10030   {
10031     case SND_MM_GAME_LEVELTIME_CHARGING:
10032       return SND_GAME_LEVELTIME_CHARGING;
10033
10034     case SND_MM_GAME_HEALTH_CHARGING:
10035       return SND_GAME_HEALTH_CHARGING;
10036
10037     default:
10038       return SND_UNDEFINED;
10039   }
10040 }
10041
10042 int map_mm_wall_element(int element)
10043 {
10044   return (element >= EL_MM_STEEL_WALL_START &&
10045           element <= EL_MM_STEEL_WALL_END ?
10046           EL_MM_STEEL_WALL :
10047
10048           element >= EL_MM_WOODEN_WALL_START &&
10049           element <= EL_MM_WOODEN_WALL_END ?
10050           EL_MM_WOODEN_WALL :
10051
10052           element >= EL_MM_ICE_WALL_START &&
10053           element <= EL_MM_ICE_WALL_END ?
10054           EL_MM_ICE_WALL :
10055
10056           element >= EL_MM_AMOEBA_WALL_START &&
10057           element <= EL_MM_AMOEBA_WALL_END ?
10058           EL_MM_AMOEBA_WALL :
10059
10060           element >= EL_DF_STEEL_WALL_START &&
10061           element <= EL_DF_STEEL_WALL_END ?
10062           EL_DF_STEEL_WALL :
10063
10064           element >= EL_DF_WOODEN_WALL_START &&
10065           element <= EL_DF_WOODEN_WALL_END ?
10066           EL_DF_WOODEN_WALL :
10067
10068           element);
10069 }
10070
10071 int map_mm_wall_element_editor(int element)
10072 {
10073   switch (element)
10074   {
10075     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
10076     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
10077     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
10078     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
10079     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
10080     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
10081
10082     default:                    return element;
10083   }
10084 }
10085
10086 int get_next_element(int element)
10087 {
10088   switch (element)
10089   {
10090     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
10091     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
10092     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
10093     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
10094     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
10095     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
10096     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
10097     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
10098     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
10099     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
10100     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
10101
10102     default:                            return element;
10103   }
10104 }
10105
10106 int el2img_mm(int element_mm)
10107 {
10108   return el2img(map_element_MM_to_RND(element_mm));
10109 }
10110
10111 int el_act2img_mm(int element_mm, int action)
10112 {
10113   return el_act2img(map_element_MM_to_RND(element_mm), action);
10114 }
10115
10116 int el_act_dir2img(int element, int action, int direction)
10117 {
10118   element = GFX_ELEMENT(element);
10119   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
10120
10121   // direction_graphic[][] == graphic[] for undefined direction graphics
10122   return element_info[element].direction_graphic[action][direction];
10123 }
10124
10125 static int el_act_dir2crm(int element, int action, int direction)
10126 {
10127   element = GFX_ELEMENT(element);
10128   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
10129
10130   // direction_graphic[][] == graphic[] for undefined direction graphics
10131   return element_info[element].direction_crumbled[action][direction];
10132 }
10133
10134 int el_act2img(int element, int action)
10135 {
10136   element = GFX_ELEMENT(element);
10137
10138   return element_info[element].graphic[action];
10139 }
10140
10141 int el_act2crm(int element, int action)
10142 {
10143   element = GFX_ELEMENT(element);
10144
10145   return element_info[element].crumbled[action];
10146 }
10147
10148 int el_dir2img(int element, int direction)
10149 {
10150   element = GFX_ELEMENT(element);
10151
10152   return el_act_dir2img(element, ACTION_DEFAULT, direction);
10153 }
10154
10155 int el2baseimg(int element)
10156 {
10157   return element_info[element].graphic[ACTION_DEFAULT];
10158 }
10159
10160 int el2img(int element)
10161 {
10162   element = GFX_ELEMENT(element);
10163
10164   return element_info[element].graphic[ACTION_DEFAULT];
10165 }
10166
10167 int el2edimg(int element)
10168 {
10169   element = GFX_ELEMENT(element);
10170
10171   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
10172 }
10173
10174 int el2edimg_with_frame(int element, int *graphic, int *frame)
10175 {
10176   *graphic = el2edimg(element);
10177   *frame = 0;
10178
10179   if (*graphic == IMG_UNKNOWN)
10180   {
10181     // no graphic defined -- if BD style, try to get runtime ("effect") element graphics
10182     // (normal BD style elements have graphics, but runtime ("effects") elements do not)
10183     int element_bd = map_element_RND_to_BD_cave(element);
10184
10185     if (element_bd != O_UNKNOWN)
10186     {
10187       struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[element_bd][0];
10188
10189       *graphic = g_bd->graphic;
10190       *frame   = g_bd->frame;
10191     }
10192   }
10193
10194   return *graphic;
10195 }
10196
10197 int el2preimg(int element)
10198 {
10199   element = GFX_ELEMENT(element);
10200
10201   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
10202 }
10203
10204 int el2panelimg(int element)
10205 {
10206   element = GFX_ELEMENT(element);
10207
10208   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
10209 }
10210
10211 int font2baseimg(int font_nr)
10212 {
10213   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
10214 }
10215
10216 int getBeltNrFromBeltElement(int element)
10217 {
10218   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
10219           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
10220           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
10221 }
10222
10223 int getBeltNrFromBeltActiveElement(int element)
10224 {
10225   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
10226           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
10227           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
10228 }
10229
10230 int getBeltNrFromBeltSwitchElement(int element)
10231 {
10232   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
10233           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
10234           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
10235 }
10236
10237 int getBeltDirNrFromBeltElement(int element)
10238 {
10239   static int belt_base_element[4] =
10240   {
10241     EL_CONVEYOR_BELT_1_LEFT,
10242     EL_CONVEYOR_BELT_2_LEFT,
10243     EL_CONVEYOR_BELT_3_LEFT,
10244     EL_CONVEYOR_BELT_4_LEFT
10245   };
10246
10247   int belt_nr = getBeltNrFromBeltElement(element);
10248   int belt_dir_nr = element - belt_base_element[belt_nr];
10249
10250   return (belt_dir_nr % 3);
10251 }
10252
10253 int getBeltDirNrFromBeltSwitchElement(int element)
10254 {
10255   static int belt_base_element[4] =
10256   {
10257     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
10258     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
10259     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
10260     EL_CONVEYOR_BELT_4_SWITCH_LEFT
10261   };
10262
10263   int belt_nr = getBeltNrFromBeltSwitchElement(element);
10264   int belt_dir_nr = element - belt_base_element[belt_nr];
10265
10266   return (belt_dir_nr % 3);
10267 }
10268
10269 int getBeltDirFromBeltElement(int element)
10270 {
10271   static int belt_move_dir[3] =
10272   {
10273     MV_LEFT,
10274     MV_NONE,
10275     MV_RIGHT
10276   };
10277
10278   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
10279
10280   return belt_move_dir[belt_dir_nr];
10281 }
10282
10283 int getBeltDirFromBeltSwitchElement(int element)
10284 {
10285   static int belt_move_dir[3] =
10286   {
10287     MV_LEFT,
10288     MV_NONE,
10289     MV_RIGHT
10290   };
10291
10292   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
10293
10294   return belt_move_dir[belt_dir_nr];
10295 }
10296
10297 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
10298 {
10299   static int belt_base_element[4] =
10300   {
10301     EL_CONVEYOR_BELT_1_LEFT,
10302     EL_CONVEYOR_BELT_2_LEFT,
10303     EL_CONVEYOR_BELT_3_LEFT,
10304     EL_CONVEYOR_BELT_4_LEFT
10305   };
10306
10307   return belt_base_element[belt_nr] + belt_dir_nr;
10308 }
10309
10310 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
10311 {
10312   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
10313
10314   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
10315 }
10316
10317 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
10318 {
10319   static int belt_base_element[4] =
10320   {
10321     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
10322     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
10323     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
10324     EL_CONVEYOR_BELT_4_SWITCH_LEFT
10325   };
10326
10327   return belt_base_element[belt_nr] + belt_dir_nr;
10328 }
10329
10330 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
10331 {
10332   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
10333
10334   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
10335 }
10336
10337 boolean swapTiles_EM(boolean is_pre_emc_cave)
10338 {
10339   return is_pre_emc_cave && leveldir_current->use_emc_tiles;
10340 }
10341
10342 boolean getTeamMode_EM(void)
10343 {
10344   return game.team_mode || network_playing;
10345 }
10346
10347 boolean isActivePlayer_EM(int player_nr)
10348 {
10349   return stored_player[player_nr].active;
10350 }
10351
10352 unsigned int InitRND(int seed)
10353 {
10354   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
10355     return InitEngineRandom_BD(seed);
10356   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10357     return InitEngineRandom_EM(seed);
10358   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10359     return InitEngineRandom_SP(seed);
10360   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
10361     return InitEngineRandom_MM(seed);
10362   else
10363     return InitEngineRandom_RND(seed);
10364 }
10365
10366 static struct Mapping_BD_to_RND_object bd_object_mapping[O_MAX_ALL];
10367 static struct Mapping_EM_to_RND_object em_object_mapping[GAME_TILE_MAX];
10368 static struct Mapping_EM_to_RND_player em_player_mapping[MAX_PLAYERS][PLY_MAX];
10369
10370 static int get_effective_element_EM(int tile, int frame_em)
10371 {
10372   int element             = em_object_mapping[tile].element_rnd;
10373   int action              = em_object_mapping[tile].action;
10374   boolean is_backside     = em_object_mapping[tile].is_backside;
10375   boolean action_removing = (action == ACTION_DIGGING ||
10376                              action == ACTION_SNAPPING ||
10377                              action == ACTION_COLLECTING);
10378
10379   if (frame_em < 7)
10380   {
10381     switch (tile)
10382     {
10383       case Xsplash_e:
10384       case Xsplash_w:
10385         return (frame_em > 5 ? EL_EMPTY : element);
10386
10387       default:
10388         return element;
10389     }
10390   }
10391   else  // frame_em == 7
10392   {
10393     switch (tile)
10394     {
10395       case Xsplash_e:
10396       case Xsplash_w:
10397         return EL_EMPTY;
10398
10399       case Ynut_stone:
10400         return EL_EMERALD;
10401
10402       case Ydiamond_stone:
10403         return EL_ROCK;
10404
10405       case Xdrip_stretch:
10406       case Xdrip_stretchB:
10407       case Ydrip_1_s:
10408       case Ydrip_1_sB:
10409       case Yball_1:
10410       case Xball_2:
10411       case Yball_2:
10412       case Yball_blank:
10413       case Ykey_1_blank:
10414       case Ykey_2_blank:
10415       case Ykey_3_blank:
10416       case Ykey_4_blank:
10417       case Ykey_5_blank:
10418       case Ykey_6_blank:
10419       case Ykey_7_blank:
10420       case Ykey_8_blank:
10421       case Ylenses_blank:
10422       case Ymagnify_blank:
10423       case Ygrass_blank:
10424       case Ydirt_blank:
10425       case Xsand_stonein_1:
10426       case Xsand_stonein_2:
10427       case Xsand_stonein_3:
10428       case Xsand_stonein_4:
10429         return element;
10430
10431       default:
10432         return (is_backside || action_removing ? EL_EMPTY : element);
10433     }
10434   }
10435 }
10436
10437 static boolean check_linear_animation_EM(int tile)
10438 {
10439   switch (tile)
10440   {
10441     case Xsand_stonesand_1:
10442     case Xsand_stonesand_quickout_1:
10443     case Xsand_sandstone_1:
10444     case Xsand_stonein_1:
10445     case Xsand_stoneout_1:
10446     case Xboom_1:
10447     case Xdynamite_1:
10448     case Ybug_w_n:
10449     case Ybug_n_e:
10450     case Ybug_e_s:
10451     case Ybug_s_w:
10452     case Ybug_e_n:
10453     case Ybug_s_e:
10454     case Ybug_w_s:
10455     case Ybug_n_w:
10456     case Ytank_w_n:
10457     case Ytank_n_e:
10458     case Ytank_e_s:
10459     case Ytank_s_w:
10460     case Ytank_e_n:
10461     case Ytank_s_e:
10462     case Ytank_w_s:
10463     case Ytank_n_w:
10464     case Xsplash_e:
10465     case Xsplash_w:
10466     case Ynut_stone:
10467       return TRUE;
10468   }
10469
10470   return FALSE;
10471 }
10472
10473 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
10474                                      boolean has_crumbled_graphics,
10475                                      int crumbled, int sync_frame)
10476 {
10477   // if element can be crumbled, but certain action graphics are just empty
10478   // space (like instantly snapping sand to empty space in 1 frame), do not
10479   // treat these empty space graphics as crumbled graphics in EMC engine
10480   if (crumbled == IMG_EMPTY_SPACE)
10481     has_crumbled_graphics = FALSE;
10482
10483   if (has_crumbled_graphics)
10484   {
10485     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
10486     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
10487                                            g_crumbled->anim_delay,
10488                                            g_crumbled->anim_mode,
10489                                            g_crumbled->anim_start_frame,
10490                                            sync_frame);
10491
10492     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
10493                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
10494
10495     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
10496     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
10497
10498     g_em->has_crumbled_graphics = TRUE;
10499   }
10500   else
10501   {
10502     g_em->crumbled_bitmap = NULL;
10503     g_em->crumbled_src_x = 0;
10504     g_em->crumbled_src_y = 0;
10505     g_em->crumbled_border_size = 0;
10506     g_em->crumbled_tile_size = 0;
10507
10508     g_em->has_crumbled_graphics = FALSE;
10509   }
10510 }
10511
10512 #if 0
10513 void ResetGfxAnimation_EM(int x, int y, int tile)
10514 {
10515   GfxFrame[x][y] = 0;
10516 }
10517 #endif
10518
10519 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
10520                         int tile, int frame_em, int x, int y)
10521 {
10522   int action = em_object_mapping[tile].action;
10523   int direction = em_object_mapping[tile].direction;
10524   int effective_element = get_effective_element_EM(tile, frame_em);
10525   int graphic = (direction == MV_NONE ?
10526                  el_act2img(effective_element, action) :
10527                  el_act_dir2img(effective_element, action, direction));
10528   struct GraphicInfo *g = &graphic_info[graphic];
10529   int sync_frame;
10530   boolean action_removing = (action == ACTION_DIGGING ||
10531                              action == ACTION_SNAPPING ||
10532                              action == ACTION_COLLECTING);
10533   boolean action_moving   = (action == ACTION_FALLING ||
10534                              action == ACTION_MOVING ||
10535                              action == ACTION_PUSHING ||
10536                              action == ACTION_EATING ||
10537                              action == ACTION_FILLING ||
10538                              action == ACTION_EMPTYING);
10539   boolean action_falling  = (action == ACTION_FALLING ||
10540                              action == ACTION_FILLING ||
10541                              action == ACTION_EMPTYING);
10542
10543   // special case: graphic uses "2nd movement tile" and has defined
10544   // 7 frames for movement animation (or less) => use default graphic
10545   // for last (8th) frame which ends the movement animation
10546   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
10547   {
10548     action = ACTION_DEFAULT;    // (keep action_* unchanged for now)
10549     graphic = (direction == MV_NONE ?
10550                el_act2img(effective_element, action) :
10551                el_act_dir2img(effective_element, action, direction));
10552
10553     g = &graphic_info[graphic];
10554   }
10555
10556   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
10557   {
10558     GfxFrame[x][y] = 0;
10559   }
10560   else if (action_moving)
10561   {
10562     boolean is_backside = em_object_mapping[tile].is_backside;
10563
10564     if (is_backside)
10565     {
10566       int direction = em_object_mapping[tile].direction;
10567       int move_dir = (action_falling ? MV_DOWN : direction);
10568
10569       GfxFrame[x][y]++;
10570
10571 #if 1
10572       // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
10573       if (g->double_movement && frame_em == 0)
10574         GfxFrame[x][y] = 0;
10575 #endif
10576
10577       if (move_dir == MV_LEFT)
10578         GfxFrame[x - 1][y] = GfxFrame[x][y];
10579       else if (move_dir == MV_RIGHT)
10580         GfxFrame[x + 1][y] = GfxFrame[x][y];
10581       else if (move_dir == MV_UP)
10582         GfxFrame[x][y - 1] = GfxFrame[x][y];
10583       else if (move_dir == MV_DOWN)
10584         GfxFrame[x][y + 1] = GfxFrame[x][y];
10585     }
10586   }
10587   else
10588   {
10589     GfxFrame[x][y]++;
10590
10591     // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
10592     if (tile == Xsand_stonesand_quickout_1 ||
10593         tile == Xsand_stonesand_quickout_2)
10594       GfxFrame[x][y]++;
10595   }
10596
10597   if (graphic_info[graphic].anim_global_sync)
10598     sync_frame = FrameCounter;
10599   else if (graphic_info[graphic].anim_global_anim_sync)
10600     sync_frame = getGlobalAnimSyncFrame();
10601   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
10602     sync_frame = GfxFrame[x][y];
10603   else
10604     sync_frame = 0;     // playfield border (pseudo steel)
10605
10606   SetRandomAnimationValue(x, y);
10607
10608   int frame = getAnimationFrame(g->anim_frames,
10609                                 g->anim_delay,
10610                                 g->anim_mode,
10611                                 g->anim_start_frame,
10612                                 sync_frame);
10613
10614   g_em->unique_identifier =
10615     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
10616 }
10617
10618 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
10619                                   int tile, int frame_em, int x, int y)
10620 {
10621   int action = em_object_mapping[tile].action;
10622   int direction = em_object_mapping[tile].direction;
10623   boolean is_backside = em_object_mapping[tile].is_backside;
10624   int effective_element = get_effective_element_EM(tile, frame_em);
10625   int effective_action = action;
10626   int graphic = (direction == MV_NONE ?
10627                  el_act2img(effective_element, effective_action) :
10628                  el_act_dir2img(effective_element, effective_action,
10629                                 direction));
10630   int crumbled = (direction == MV_NONE ?
10631                   el_act2crm(effective_element, effective_action) :
10632                   el_act_dir2crm(effective_element, effective_action,
10633                                  direction));
10634   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10635   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10636   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10637   struct GraphicInfo *g = &graphic_info[graphic];
10638   int sync_frame;
10639
10640   // special case: graphic uses "2nd movement tile" and has defined
10641   // 7 frames for movement animation (or less) => use default graphic
10642   // for last (8th) frame which ends the movement animation
10643   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
10644   {
10645     effective_action = ACTION_DEFAULT;
10646     graphic = (direction == MV_NONE ?
10647                el_act2img(effective_element, effective_action) :
10648                el_act_dir2img(effective_element, effective_action,
10649                               direction));
10650     crumbled = (direction == MV_NONE ?
10651                 el_act2crm(effective_element, effective_action) :
10652                 el_act_dir2crm(effective_element, effective_action,
10653                                direction));
10654
10655     g = &graphic_info[graphic];
10656   }
10657
10658   if (graphic_info[graphic].anim_global_sync)
10659     sync_frame = FrameCounter;
10660   else if (graphic_info[graphic].anim_global_anim_sync)
10661     sync_frame = getGlobalAnimSyncFrame();
10662   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
10663     sync_frame = GfxFrame[x][y];
10664   else
10665     sync_frame = 0;     // playfield border (pseudo steel)
10666
10667   SetRandomAnimationValue(x, y);
10668
10669   int frame = getAnimationFrame(g->anim_frames,
10670                                 g->anim_delay,
10671                                 g->anim_mode,
10672                                 g->anim_start_frame,
10673                                 sync_frame);
10674
10675   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
10676                       g->double_movement && is_backside);
10677
10678   // (updating the "crumbled" graphic definitions is probably not really needed,
10679   // as animations for crumbled graphics can't be longer than one EMC cycle)
10680   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10681                            sync_frame);
10682 }
10683
10684 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
10685                                   int player_nr, int anim, int frame_em)
10686 {
10687   int element   = em_player_mapping[player_nr][anim].element_rnd;
10688   int action    = em_player_mapping[player_nr][anim].action;
10689   int direction = em_player_mapping[player_nr][anim].direction;
10690   int graphic = (direction == MV_NONE ?
10691                  el_act2img(element, action) :
10692                  el_act_dir2img(element, action, direction));
10693   struct GraphicInfo *g = &graphic_info[graphic];
10694   int sync_frame;
10695
10696   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
10697
10698   stored_player[player_nr].StepFrame = frame_em;
10699
10700   sync_frame = stored_player[player_nr].Frame;
10701
10702   int frame = getAnimationFrame(g->anim_frames,
10703                                 g->anim_delay,
10704                                 g->anim_mode,
10705                                 g->anim_start_frame,
10706                                 sync_frame);
10707
10708   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
10709                       &g_em->src_x, &g_em->src_y, FALSE);
10710 }
10711
10712 #define BD_GFX_RANGE(a, n, i)           ((i) >= (a) && (i) < (a) + (n))
10713 #define BD_GFX_FRAME(b, i)              (((i) - (b)) * 8)
10714
10715 void InitGraphicInfo_BD(void)
10716 {
10717   int i, j;
10718
10719   if (graphic_info == NULL)             // still at startup phase
10720     return;
10721
10722   // always start with reliable default values
10723   for (i = 0; i < O_MAX_ALL; i++)
10724   {
10725     bd_object_mapping[i].element_rnd = EL_UNKNOWN;
10726     bd_object_mapping[i].action = ACTION_DEFAULT;
10727     bd_object_mapping[i].direction = MV_NONE;
10728   }
10729
10730   for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
10731   {
10732     int e = bd_object_mapping_list[i].element_bd;
10733
10734     bd_object_mapping[e].element_rnd = bd_object_mapping_list[i].element_rnd;
10735
10736     if (bd_object_mapping_list[i].action != -1)
10737       bd_object_mapping[e].action = bd_object_mapping_list[i].action;
10738
10739     if (bd_object_mapping_list[i].direction != -1)
10740       bd_object_mapping[e].direction =
10741         MV_DIR_FROM_BIT(bd_object_mapping_list[i].direction);
10742   }
10743
10744   for (i = 0; i < O_MAX_ALL; i++)
10745   {
10746     int element = bd_object_mapping[i].element_rnd;
10747     int action = bd_object_mapping[i].action;
10748     int direction = bd_object_mapping[i].direction;
10749
10750     for (j = 0; j < 8; j++)
10751     {
10752       int effective_element = element;
10753       int effective_action = action;
10754       int graphic = (el_act_dir2img(effective_element, effective_action,
10755                                     direction));
10756       struct GraphicInfo *g = &graphic_info[graphic];
10757       struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[i][j];
10758       Bitmap *src_bitmap;
10759       int src_x, src_y;
10760       int sync_frame = (BD_GFX_RANGE(O_PRE_PL_1, 3, i)        ? BD_GFX_FRAME(O_PRE_PL_1, i) :
10761                         BD_GFX_RANGE(O_PRE_DIA_1, 5, i)       ? BD_GFX_FRAME(O_PRE_DIA_1, i) :
10762                         BD_GFX_RANGE(O_PRE_STONE_1, 4, i)     ? BD_GFX_FRAME(O_PRE_STONE_1, i) :
10763                         BD_GFX_RANGE(O_PRE_STEEL_1, 4, i)     ? BD_GFX_FRAME(O_PRE_STEEL_1, i) :
10764                         BD_GFX_RANGE(O_BOMB_TICK_1, 7, i)     ? BD_GFX_FRAME(O_BOMB_TICK_1, i) :
10765                         BD_GFX_RANGE(O_BOMB_EXPL_1, 4, i)     ? BD_GFX_FRAME(O_BOMB_EXPL_1, i) :
10766                         BD_GFX_RANGE(O_NUT_EXPL_1, 4, i)      ? BD_GFX_FRAME(O_NUT_EXPL_1, i) :
10767                         BD_GFX_RANGE(O_GHOST_EXPL_1, 4, i)    ? BD_GFX_FRAME(O_GHOST_EXPL_1, i) :
10768                         BD_GFX_RANGE(O_EXPLODE_1, 5, i)       ? BD_GFX_FRAME(O_EXPLODE_1, i) :
10769                         BD_GFX_RANGE(O_PRE_CLOCK_1, 4, i)     ? BD_GFX_FRAME(O_PRE_CLOCK_1, i) :
10770                         BD_GFX_RANGE(O_NITRO_EXPL_1, 4, i)    ? BD_GFX_FRAME(O_NITRO_EXPL_1, i) :
10771                         BD_GFX_RANGE(O_AMOEBA_2_EXPL_1, 4, i) ? BD_GFX_FRAME(O_AMOEBA_2_EXPL_1, i):
10772                         i == O_INBOX_OPEN || i == O_OUTBOX_OPEN ? j :
10773                         j * 2);
10774       int frame = getAnimationFrame(g->anim_frames,
10775                                     g->anim_delay,
10776                                     g->anim_mode,
10777                                     g->anim_start_frame,
10778                                     sync_frame);
10779
10780       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10781
10782       g_bd->bitmap = src_bitmap;
10783       g_bd->src_x  = src_x;
10784       g_bd->src_y  = src_y;
10785       g_bd->width  = TILEX;
10786       g_bd->height = TILEY;
10787
10788       g_bd->graphic = graphic;
10789       g_bd->frame = frame;
10790     }
10791   }
10792
10793   // game graphics template for level-specific colors for native BD levels
10794   int graphic = IMG_BDX_GAME_GRAPHICS_COLOR_TEMPLATE;
10795   struct GraphicInfo_BD *g_bd = &graphic_info_bd_color_template;
10796   Bitmap *src_bitmap;
10797   int src_x, src_y;
10798
10799   getGraphicSourceExt(graphic, 0, &src_bitmap, &src_x, &src_y, FALSE);
10800
10801   g_bd->bitmap = src_bitmap;
10802   g_bd->src_x  = src_x;
10803   g_bd->src_y  = src_y;
10804   g_bd->width  = TILEX;
10805   g_bd->height = TILEY;
10806
10807   g_bd->graphic = graphic;
10808   g_bd->frame = 0;
10809 }
10810
10811 void InitGraphicInfo_EM(void)
10812 {
10813   int i, j, p;
10814
10815   // always start with reliable default values
10816   for (i = 0; i < GAME_TILE_MAX; i++)
10817   {
10818     em_object_mapping[i].element_rnd = EL_UNKNOWN;
10819     em_object_mapping[i].is_backside = FALSE;
10820     em_object_mapping[i].action = ACTION_DEFAULT;
10821     em_object_mapping[i].direction = MV_NONE;
10822   }
10823
10824   // always start with reliable default values
10825   for (p = 0; p < MAX_PLAYERS; p++)
10826   {
10827     for (i = 0; i < PLY_MAX; i++)
10828     {
10829       em_player_mapping[p][i].element_rnd = EL_UNKNOWN;
10830       em_player_mapping[p][i].action = ACTION_DEFAULT;
10831       em_player_mapping[p][i].direction = MV_NONE;
10832     }
10833   }
10834
10835   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
10836   {
10837     int e = em_object_mapping_list[i].element_em;
10838
10839     em_object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
10840     em_object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
10841
10842     if (em_object_mapping_list[i].action != -1)
10843       em_object_mapping[e].action = em_object_mapping_list[i].action;
10844
10845     if (em_object_mapping_list[i].direction != -1)
10846       em_object_mapping[e].direction =
10847         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
10848   }
10849
10850   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
10851   {
10852     int a = em_player_mapping_list[i].action_em;
10853     int p = em_player_mapping_list[i].player_nr;
10854
10855     em_player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
10856
10857     if (em_player_mapping_list[i].action != -1)
10858       em_player_mapping[p][a].action = em_player_mapping_list[i].action;
10859
10860     if (em_player_mapping_list[i].direction != -1)
10861       em_player_mapping[p][a].direction =
10862         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
10863   }
10864
10865   for (i = 0; i < GAME_TILE_MAX; i++)
10866   {
10867     int element = em_object_mapping[i].element_rnd;
10868     int action = em_object_mapping[i].action;
10869     int direction = em_object_mapping[i].direction;
10870     boolean is_backside = em_object_mapping[i].is_backside;
10871     boolean action_exploding = ((action == ACTION_EXPLODING ||
10872                                  action == ACTION_SMASHED_BY_ROCK ||
10873                                  action == ACTION_SMASHED_BY_SPRING) &&
10874                                 element != EL_DIAMOND);
10875     boolean action_active = (action == ACTION_ACTIVE);
10876     boolean action_other = (action == ACTION_OTHER);
10877
10878     for (j = 0; j < 8; j++)
10879     {
10880       int effective_element = get_effective_element_EM(i, j);
10881       int effective_action = (j < 7 ? action :
10882                               i == Xdrip_stretch ? action :
10883                               i == Xdrip_stretchB ? action :
10884                               i == Ydrip_1_s ? action :
10885                               i == Ydrip_1_sB ? action :
10886                               i == Yball_1 ? action :
10887                               i == Xball_2 ? action :
10888                               i == Yball_2 ? action :
10889                               i == Yball_blank ? action :
10890                               i == Ykey_1_blank ? action :
10891                               i == Ykey_2_blank ? action :
10892                               i == Ykey_3_blank ? action :
10893                               i == Ykey_4_blank ? action :
10894                               i == Ykey_5_blank ? action :
10895                               i == Ykey_6_blank ? action :
10896                               i == Ykey_7_blank ? action :
10897                               i == Ykey_8_blank ? action :
10898                               i == Ylenses_blank ? action :
10899                               i == Ymagnify_blank ? action :
10900                               i == Ygrass_blank ? action :
10901                               i == Ydirt_blank ? action :
10902                               i == Xsand_stonein_1 ? action :
10903                               i == Xsand_stonein_2 ? action :
10904                               i == Xsand_stonein_3 ? action :
10905                               i == Xsand_stonein_4 ? action :
10906                               i == Xsand_stoneout_1 ? action :
10907                               i == Xsand_stoneout_2 ? action :
10908                               i == Xboom_android ? ACTION_EXPLODING :
10909                               action_exploding ? ACTION_EXPLODING :
10910                               action_active ? action :
10911                               action_other ? action :
10912                               ACTION_DEFAULT);
10913       int graphic = (el_act_dir2img(effective_element, effective_action,
10914                                     direction));
10915       int crumbled = (el_act_dir2crm(effective_element, effective_action,
10916                                      direction));
10917       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10918       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10919       boolean has_action_graphics = (graphic != base_graphic);
10920       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10921       struct GraphicInfo *g = &graphic_info[graphic];
10922       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10923       Bitmap *src_bitmap;
10924       int src_x, src_y;
10925       // ensure to get symmetric 3-frame, 2-delay animations as used in EM
10926       boolean special_animation = (action != ACTION_DEFAULT &&
10927                                    g->anim_frames == 3 &&
10928                                    g->anim_delay == 2 &&
10929                                    g->anim_mode & ANIM_LINEAR);
10930       int sync_frame = (i == Xdrip_stretch ? 7 :
10931                         i == Xdrip_stretchB ? 7 :
10932                         i == Ydrip_2_s ? j + 8 :
10933                         i == Ydrip_2_sB ? j + 8 :
10934                         i == Xacid_1 ? 0 :
10935                         i == Xacid_2 ? 10 :
10936                         i == Xacid_3 ? 20 :
10937                         i == Xacid_4 ? 30 :
10938                         i == Xacid_5 ? 40 :
10939                         i == Xacid_6 ? 50 :
10940                         i == Xacid_7 ? 60 :
10941                         i == Xacid_8 ? 70 :
10942                         i == Xfake_acid_1 ? 0 :
10943                         i == Xfake_acid_2 ? 10 :
10944                         i == Xfake_acid_3 ? 20 :
10945                         i == Xfake_acid_4 ? 30 :
10946                         i == Xfake_acid_5 ? 40 :
10947                         i == Xfake_acid_6 ? 50 :
10948                         i == Xfake_acid_7 ? 60 :
10949                         i == Xfake_acid_8 ? 70 :
10950                         i == Xfake_acid_1_player ? 0 :
10951                         i == Xfake_acid_2_player ? 10 :
10952                         i == Xfake_acid_3_player ? 20 :
10953                         i == Xfake_acid_4_player ? 30 :
10954                         i == Xfake_acid_5_player ? 40 :
10955                         i == Xfake_acid_6_player ? 50 :
10956                         i == Xfake_acid_7_player ? 60 :
10957                         i == Xfake_acid_8_player ? 70 :
10958                         i == Xball_2 ? 7 :
10959                         i == Yball_2 ? j + 8 :
10960                         i == Yball_blank ? j + 1 :
10961                         i == Ykey_1_blank ? j + 1 :
10962                         i == Ykey_2_blank ? j + 1 :
10963                         i == Ykey_3_blank ? j + 1 :
10964                         i == Ykey_4_blank ? j + 1 :
10965                         i == Ykey_5_blank ? j + 1 :
10966                         i == Ykey_6_blank ? j + 1 :
10967                         i == Ykey_7_blank ? j + 1 :
10968                         i == Ykey_8_blank ? j + 1 :
10969                         i == Ylenses_blank ? j + 1 :
10970                         i == Ymagnify_blank ? j + 1 :
10971                         i == Ygrass_blank ? j + 1 :
10972                         i == Ydirt_blank ? j + 1 :
10973                         i == Xamoeba_1 ? 0 :
10974                         i == Xamoeba_2 ? 1 :
10975                         i == Xamoeba_3 ? 2 :
10976                         i == Xamoeba_4 ? 3 :
10977                         i == Xamoeba_5 ? 0 :
10978                         i == Xamoeba_6 ? 1 :
10979                         i == Xamoeba_7 ? 2 :
10980                         i == Xamoeba_8 ? 3 :
10981                         i == Xexit_2 ? j + 8 :
10982                         i == Xexit_3 ? j + 16 :
10983                         i == Xdynamite_1 ? 0 :
10984                         i == Xdynamite_2 ? 8 :
10985                         i == Xdynamite_3 ? 16 :
10986                         i == Xdynamite_4 ? 24 :
10987                         i == Xsand_stonein_1 ? j + 1 :
10988                         i == Xsand_stonein_2 ? j + 9 :
10989                         i == Xsand_stonein_3 ? j + 17 :
10990                         i == Xsand_stonein_4 ? j + 25 :
10991                         i == Xsand_stoneout_1 && j == 0 ? 0 :
10992                         i == Xsand_stoneout_1 && j == 1 ? 0 :
10993                         i == Xsand_stoneout_1 && j == 2 ? 1 :
10994                         i == Xsand_stoneout_1 && j == 3 ? 2 :
10995                         i == Xsand_stoneout_1 && j == 4 ? 2 :
10996                         i == Xsand_stoneout_1 && j == 5 ? 3 :
10997                         i == Xsand_stoneout_1 && j == 6 ? 4 :
10998                         i == Xsand_stoneout_1 && j == 7 ? 4 :
10999                         i == Xsand_stoneout_2 && j == 0 ? 5 :
11000                         i == Xsand_stoneout_2 && j == 1 ? 6 :
11001                         i == Xsand_stoneout_2 && j == 2 ? 7 :
11002                         i == Xsand_stoneout_2 && j == 3 ? 8 :
11003                         i == Xsand_stoneout_2 && j == 4 ? 9 :
11004                         i == Xsand_stoneout_2 && j == 5 ? 11 :
11005                         i == Xsand_stoneout_2 && j == 6 ? 13 :
11006                         i == Xsand_stoneout_2 && j == 7 ? 15 :
11007                         i == Xboom_bug && j == 1 ? 2 :
11008                         i == Xboom_bug && j == 2 ? 2 :
11009                         i == Xboom_bug && j == 3 ? 4 :
11010                         i == Xboom_bug && j == 4 ? 4 :
11011                         i == Xboom_bug && j == 5 ? 2 :
11012                         i == Xboom_bug && j == 6 ? 2 :
11013                         i == Xboom_bug && j == 7 ? 0 :
11014                         i == Xboom_tank && j == 1 ? 2 :
11015                         i == Xboom_tank && j == 2 ? 2 :
11016                         i == Xboom_tank && j == 3 ? 4 :
11017                         i == Xboom_tank && j == 4 ? 4 :
11018                         i == Xboom_tank && j == 5 ? 2 :
11019                         i == Xboom_tank && j == 6 ? 2 :
11020                         i == Xboom_tank && j == 7 ? 0 :
11021                         i == Xboom_android && j == 7 ? 6 :
11022                         i == Xboom_1 && j == 1 ? 2 :
11023                         i == Xboom_1 && j == 2 ? 2 :
11024                         i == Xboom_1 && j == 3 ? 4 :
11025                         i == Xboom_1 && j == 4 ? 4 :
11026                         i == Xboom_1 && j == 5 ? 6 :
11027                         i == Xboom_1 && j == 6 ? 6 :
11028                         i == Xboom_1 && j == 7 ? 8 :
11029                         i == Xboom_2 && j == 0 ? 8 :
11030                         i == Xboom_2 && j == 1 ? 8 :
11031                         i == Xboom_2 && j == 2 ? 10 :
11032                         i == Xboom_2 && j == 3 ? 10 :
11033                         i == Xboom_2 && j == 4 ? 10 :
11034                         i == Xboom_2 && j == 5 ? 12 :
11035                         i == Xboom_2 && j == 6 ? 12 :
11036                         i == Xboom_2 && j == 7 ? 12 :
11037                         special_animation && j == 4 ? 3 :
11038                         effective_action != action ? 0 :
11039                         j);
11040       int frame = getAnimationFrame(g->anim_frames,
11041                                     g->anim_delay,
11042                                     g->anim_mode,
11043                                     g->anim_start_frame,
11044                                     sync_frame);
11045
11046       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
11047                           g->double_movement && is_backside);
11048
11049       g_em->bitmap = src_bitmap;
11050       g_em->src_x = src_x;
11051       g_em->src_y = src_y;
11052       g_em->src_offset_x = 0;
11053       g_em->src_offset_y = 0;
11054       g_em->dst_offset_x = 0;
11055       g_em->dst_offset_y = 0;
11056       g_em->width  = TILEX;
11057       g_em->height = TILEY;
11058
11059       g_em->preserve_background = FALSE;
11060
11061       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
11062                                sync_frame);
11063
11064       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
11065                                    effective_action == ACTION_MOVING  ||
11066                                    effective_action == ACTION_PUSHING ||
11067                                    effective_action == ACTION_EATING)) ||
11068           (!has_action_graphics && (effective_action == ACTION_FILLING ||
11069                                     effective_action == ACTION_EMPTYING)))
11070       {
11071         int move_dir =
11072           (effective_action == ACTION_FALLING ||
11073            effective_action == ACTION_FILLING ||
11074            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
11075         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
11076         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
11077         int num_steps = (i == Ydrip_1_s  ? 16 :
11078                          i == Ydrip_1_sB ? 16 :
11079                          i == Ydrip_2_s  ? 16 :
11080                          i == Ydrip_2_sB ? 16 :
11081                          i == Xsand_stonein_1 ? 32 :
11082                          i == Xsand_stonein_2 ? 32 :
11083                          i == Xsand_stonein_3 ? 32 :
11084                          i == Xsand_stonein_4 ? 32 :
11085                          i == Xsand_stoneout_1 ? 16 :
11086                          i == Xsand_stoneout_2 ? 16 : 8);
11087         int cx = ABS(dx) * (TILEX / num_steps);
11088         int cy = ABS(dy) * (TILEY / num_steps);
11089         int step_frame = (i == Ydrip_2_s        ? j + 8 :
11090                           i == Ydrip_2_sB       ? j + 8 :
11091                           i == Xsand_stonein_2  ? j + 8 :
11092                           i == Xsand_stonein_3  ? j + 16 :
11093                           i == Xsand_stonein_4  ? j + 24 :
11094                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
11095         int step = (is_backside ? step_frame : num_steps - step_frame);
11096
11097         if (is_backside)        // tile where movement starts
11098         {
11099           if (dx < 0 || dy < 0)
11100           {
11101             g_em->src_offset_x = cx * step;
11102             g_em->src_offset_y = cy * step;
11103           }
11104           else
11105           {
11106             g_em->dst_offset_x = cx * step;
11107             g_em->dst_offset_y = cy * step;
11108           }
11109         }
11110         else                    // tile where movement ends
11111         {
11112           if (dx < 0 || dy < 0)
11113           {
11114             g_em->dst_offset_x = cx * step;
11115             g_em->dst_offset_y = cy * step;
11116           }
11117           else
11118           {
11119             g_em->src_offset_x = cx * step;
11120             g_em->src_offset_y = cy * step;
11121           }
11122         }
11123
11124         g_em->width  = TILEX - cx * step;
11125         g_em->height = TILEY - cy * step;
11126       }
11127
11128       // create unique graphic identifier to decide if tile must be redrawn
11129       /* bit 31 - 16 (16 bit): EM style graphic
11130          bit 15 - 12 ( 4 bit): EM style frame
11131          bit 11 -  6 ( 6 bit): graphic width
11132          bit  5 -  0 ( 6 bit): graphic height */
11133       g_em->unique_identifier =
11134         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
11135     }
11136   }
11137
11138   for (i = 0; i < GAME_TILE_MAX; i++)
11139   {
11140     for (j = 0; j < 8; j++)
11141     {
11142       int element = em_object_mapping[i].element_rnd;
11143       int action = em_object_mapping[i].action;
11144       int direction = em_object_mapping[i].direction;
11145       boolean is_backside = em_object_mapping[i].is_backside;
11146       int graphic_action  = el_act_dir2img(element, action, direction);
11147       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
11148
11149       if ((action == ACTION_SMASHED_BY_ROCK ||
11150            action == ACTION_SMASHED_BY_SPRING ||
11151            action == ACTION_EATING) &&
11152           graphic_action == graphic_default)
11153       {
11154         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
11155                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
11156                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
11157                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
11158                  Xspring);
11159
11160         // no separate animation for "smashed by rock" -- use rock instead
11161         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
11162         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
11163
11164         g_em->bitmap            = g_xx->bitmap;
11165         g_em->src_x             = g_xx->src_x;
11166         g_em->src_y             = g_xx->src_y;
11167         g_em->src_offset_x      = g_xx->src_offset_x;
11168         g_em->src_offset_y      = g_xx->src_offset_y;
11169         g_em->dst_offset_x      = g_xx->dst_offset_x;
11170         g_em->dst_offset_y      = g_xx->dst_offset_y;
11171         g_em->width             = g_xx->width;
11172         g_em->height            = g_xx->height;
11173         g_em->unique_identifier = g_xx->unique_identifier;
11174
11175         if (!is_backside)
11176           g_em->preserve_background = TRUE;
11177       }
11178     }
11179   }
11180
11181   for (p = 0; p < MAX_PLAYERS; p++)
11182   {
11183     for (i = 0; i < PLY_MAX; i++)
11184     {
11185       int element = em_player_mapping[p][i].element_rnd;
11186       int action = em_player_mapping[p][i].action;
11187       int direction = em_player_mapping[p][i].direction;
11188
11189       for (j = 0; j < 8; j++)
11190       {
11191         int effective_element = element;
11192         int effective_action = action;
11193         int graphic = (direction == MV_NONE ?
11194                        el_act2img(effective_element, effective_action) :
11195                        el_act_dir2img(effective_element, effective_action,
11196                                       direction));
11197         struct GraphicInfo *g = &graphic_info[graphic];
11198         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
11199         Bitmap *src_bitmap;
11200         int src_x, src_y;
11201         int sync_frame = j;
11202         int frame = getAnimationFrame(g->anim_frames,
11203                                       g->anim_delay,
11204                                       g->anim_mode,
11205                                       g->anim_start_frame,
11206                                       sync_frame);
11207
11208         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
11209
11210         g_em->bitmap = src_bitmap;
11211         g_em->src_x = src_x;
11212         g_em->src_y = src_y;
11213         g_em->src_offset_x = 0;
11214         g_em->src_offset_y = 0;
11215         g_em->dst_offset_x = 0;
11216         g_em->dst_offset_y = 0;
11217         g_em->width  = TILEX;
11218         g_em->height = TILEY;
11219       }
11220     }
11221   }
11222 }
11223
11224 static void CheckSaveEngineSnapshot_BD(boolean frame_max,
11225                                        boolean player_moving,
11226                                        boolean player_snapping)
11227 {
11228   if (frame_max)
11229   {
11230     if (!local_player->was_waiting)
11231     {
11232       if (!CheckSaveEngineSnapshotToList())
11233         return;
11234
11235       local_player->was_waiting = TRUE;
11236     }
11237   }
11238   else if (player_moving || player_snapping)
11239   {
11240     local_player->was_waiting = FALSE;
11241   }
11242 }
11243
11244 static void CheckSaveEngineSnapshot_EM(int frame,
11245                                        boolean any_player_moving,
11246                                        boolean any_player_snapping,
11247                                        boolean any_player_dropping)
11248 {
11249   if (frame == 7 && !any_player_dropping)
11250   {
11251     if (!local_player->was_waiting)
11252     {
11253       if (!CheckSaveEngineSnapshotToList())
11254         return;
11255
11256       local_player->was_waiting = TRUE;
11257     }
11258   }
11259   else if (any_player_moving || any_player_snapping || any_player_dropping)
11260   {
11261     local_player->was_waiting = FALSE;
11262   }
11263 }
11264
11265 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
11266                                        boolean murphy_is_dropping)
11267 {
11268   if (murphy_is_waiting)
11269   {
11270     if (!local_player->was_waiting)
11271     {
11272       if (!CheckSaveEngineSnapshotToList())
11273         return;
11274
11275       local_player->was_waiting = TRUE;
11276     }
11277   }
11278   else
11279   {
11280     local_player->was_waiting = FALSE;
11281   }
11282 }
11283
11284 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
11285                                        boolean button_released)
11286 {
11287   if (button_released)
11288   {
11289     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
11290       CheckSaveEngineSnapshotToList();
11291   }
11292   else if (element_clicked)
11293   {
11294     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
11295       CheckSaveEngineSnapshotToList();
11296
11297     game.snapshot.changed_action = TRUE;
11298   }
11299 }
11300
11301 boolean CheckSingleStepMode_BD(boolean frame_max,
11302                                boolean player_moving,
11303                                boolean player_snapping)
11304 {
11305   if (tape.single_step && tape.recording && !tape.pausing)
11306     if (frame_max && FrameCounter > 6)
11307       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11308
11309   CheckSaveEngineSnapshot_BD(frame_max, player_moving, player_snapping);
11310
11311   return tape.pausing;
11312 }
11313
11314 boolean CheckSingleStepMode_EM(int frame,
11315                                boolean any_player_moving,
11316                                boolean any_player_snapping,
11317                                boolean any_player_dropping)
11318 {
11319   if (tape.single_step && tape.recording && !tape.pausing)
11320     if (frame == 7 && !any_player_dropping && FrameCounter > 6)
11321       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11322
11323   CheckSaveEngineSnapshot_EM(frame, any_player_moving,
11324                              any_player_snapping, any_player_dropping);
11325
11326   return tape.pausing;
11327 }
11328
11329 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
11330                             boolean murphy_is_dropping)
11331 {
11332   boolean murphy_starts_dropping = FALSE;
11333   int i;
11334
11335   for (i = 0; i < MAX_PLAYERS; i++)
11336     if (stored_player[i].force_dropping)
11337       murphy_starts_dropping = TRUE;
11338
11339   if (tape.single_step && tape.recording && !tape.pausing)
11340     if (murphy_is_waiting && !murphy_starts_dropping)
11341       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11342
11343   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
11344 }
11345
11346 void CheckSingleStepMode_MM(boolean element_clicked,
11347                             boolean button_released)
11348 {
11349   if (tape.single_step && tape.recording && !tape.pausing)
11350     if (button_released)
11351       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11352
11353   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
11354 }
11355
11356 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
11357                          int graphic, int sync_frame)
11358 {
11359   int frame = getGraphicAnimationFrame(graphic, sync_frame);
11360
11361   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
11362 }
11363
11364 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
11365 {
11366   return (IS_NEXT_FRAME(sync_frame, graphic));
11367 }
11368
11369 int getGraphicInfo_Delay(int graphic)
11370 {
11371   return graphic_info[graphic].anim_delay;
11372 }
11373
11374 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
11375 {
11376   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
11377     return FALSE;
11378
11379   if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
11380     return FALSE;
11381
11382   return TRUE;
11383 }
11384
11385 void PlayMenuSoundExt(int sound)
11386 {
11387   if (sound == SND_UNDEFINED)
11388     return;
11389
11390   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11391       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11392     return;
11393
11394   if (IS_LOOP_SOUND(sound))
11395     PlaySoundLoop(sound);
11396   else
11397     PlaySound(sound);
11398 }
11399
11400 void PlayMenuSound(void)
11401 {
11402   PlayMenuSoundExt(menu.sound[game_status]);
11403 }
11404
11405 void PlayMenuSoundStereo(int sound, int stereo_position)
11406 {
11407   if (sound == SND_UNDEFINED)
11408     return;
11409
11410   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11411       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11412     return;
11413
11414   if (IS_LOOP_SOUND(sound))
11415     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
11416   else
11417     PlaySoundStereo(sound, stereo_position);
11418 }
11419
11420 void PlayMenuSoundIfLoopExt(int sound)
11421 {
11422   if (sound == SND_UNDEFINED)
11423     return;
11424
11425   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11426       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11427     return;
11428
11429   if (IS_LOOP_SOUND(sound))
11430     PlaySoundLoop(sound);
11431 }
11432
11433 void PlayMenuSoundIfLoop(void)
11434 {
11435   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
11436 }
11437
11438 void PlayMenuMusicExt(int music)
11439 {
11440   if (music == MUS_UNDEFINED)
11441     return;
11442
11443   if (!setup.sound_music)
11444     return;
11445
11446   if (IS_LOOP_MUSIC(music))
11447     PlayMusicLoop(music);
11448   else
11449     PlayMusic(music);
11450 }
11451
11452 void PlayMenuMusic(void)
11453 {
11454   char *curr_music = getCurrentlyPlayingMusicFilename();
11455   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
11456
11457   if (!strEqual(curr_music, next_music))
11458     PlayMenuMusicExt(menu.music[game_status]);
11459 }
11460
11461 void PlayMenuSoundsAndMusic(void)
11462 {
11463   PlayMenuSound();
11464   PlayMenuMusic();
11465 }
11466
11467 static void FadeMenuSounds(void)
11468 {
11469   FadeSounds();
11470 }
11471
11472 static void FadeMenuMusic(void)
11473 {
11474   char *curr_music = getCurrentlyPlayingMusicFilename();
11475   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
11476
11477   if (!strEqual(curr_music, next_music))
11478     FadeMusic();
11479 }
11480
11481 void FadeMenuSoundsAndMusic(void)
11482 {
11483   FadeMenuSounds();
11484   FadeMenuMusic();
11485 }
11486
11487 void PlaySoundActivating(void)
11488 {
11489 #if 0
11490   PlaySound(SND_MENU_ITEM_ACTIVATING);
11491 #endif
11492 }
11493
11494 void PlaySoundSelecting(void)
11495 {
11496 #if 0
11497   PlaySound(SND_MENU_ITEM_SELECTING);
11498 #endif
11499 }
11500
11501 void ToggleAudioSampleRateIfNeeded(void)
11502 {
11503   int setup_audio_sample_rate = (setup.audio_sample_rate_44100 ? 44100 : 22050);
11504
11505   // if setup and audio sample rate are already matching, nothing do do
11506   if ((setup_audio_sample_rate == audio.sample_rate) ||
11507       !audio.sound_available)
11508     return;
11509
11510 #if 1
11511   // apparently changing the audio output sample rate does not work at runtime,
11512   // so currently the program has to be restarted to apply the new sample rate
11513   Request("Please restart the program to change audio sample rate!", REQ_CONFIRM);
11514 #else
11515   SDLReopenAudio();
11516
11517   // set setup value according to successfully changed audio sample rate
11518   setup.audio_sample_rate_44100 = (audio.sample_rate == 44100);
11519 #endif
11520 }
11521
11522 void ToggleFullscreenIfNeeded(void)
11523 {
11524   // if setup and video fullscreen state are already matching, nothing do do
11525   if (setup.fullscreen == video.fullscreen_enabled ||
11526       !video.fullscreen_available)
11527     return;
11528
11529   SDLSetWindowFullscreen(setup.fullscreen);
11530
11531   // set setup value according to successfully changed fullscreen mode
11532   setup.fullscreen = video.fullscreen_enabled;
11533 }
11534
11535 void ChangeWindowScalingIfNeeded(void)
11536 {
11537   // if setup and video window scaling are already matching, nothing do do
11538   if (setup.window_scaling_percent == video.window_scaling_percent ||
11539       video.fullscreen_enabled)
11540     return;
11541
11542   SDLSetWindowScaling(setup.window_scaling_percent);
11543
11544   // set setup value according to successfully changed window scaling
11545   setup.window_scaling_percent = video.window_scaling_percent;
11546 }
11547
11548 void ChangeVsyncModeIfNeeded(void)
11549 {
11550   int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
11551   int video_vsync_mode = video.vsync_mode;
11552
11553   // if setup and video vsync mode are already matching, nothing do do
11554   if (setup_vsync_mode == video_vsync_mode)
11555     return;
11556
11557   // if renderer is using OpenGL, vsync mode can directly be changed
11558   SDLSetScreenVsyncMode(setup.vsync_mode);
11559
11560   // if vsync mode unchanged, try re-creating renderer to set vsync mode
11561   if (video.vsync_mode == video_vsync_mode)
11562   {
11563     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
11564
11565     // save backbuffer content which gets lost when re-creating screen
11566     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11567
11568     // force re-creating screen and renderer to set new vsync mode
11569     video.fullscreen_enabled = !setup.fullscreen;
11570
11571     // when creating new renderer, destroy textures linked to old renderer
11572     FreeAllImageTextures();     // needs old renderer to free the textures
11573
11574     // re-create screen and renderer (including change of vsync mode)
11575     ChangeVideoModeIfNeeded(setup.fullscreen);
11576
11577     // set setup value according to successfully changed fullscreen mode
11578     setup.fullscreen = video.fullscreen_enabled;
11579
11580     // restore backbuffer content from temporary backbuffer backup bitmap
11581     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11582     FreeBitmap(tmp_backbuffer);
11583
11584     // update visible window/screen
11585     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11586
11587     // when changing vsync mode, re-create textures for new renderer
11588     InitImageTextures();
11589   }
11590
11591   // set setup value according to successfully changed vsync mode
11592   setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
11593 }
11594
11595 static void JoinRectangles(int *x, int *y, int *width, int *height,
11596                            int x2, int y2, int width2, int height2)
11597 {
11598   // do not join with "off-screen" rectangle
11599   if (x2 == -1 || y2 == -1)
11600     return;
11601
11602   *x = MIN(*x, x2);
11603   *y = MIN(*y, y2);
11604   *width = MAX(*width, width2);
11605   *height = MAX(*height, height2);
11606 }
11607
11608 void SetAnimStatus(int anim_status_new)
11609 {
11610   if (anim_status_new == GAME_MODE_MAIN)
11611     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
11612   else if (anim_status_new == GAME_MODE_NAMES)
11613     anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
11614   else if (anim_status_new == GAME_MODE_SCORES)
11615     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
11616
11617   global.anim_status_next = anim_status_new;
11618
11619   // directly set screen modes that are entered without fading
11620   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
11621        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
11622       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
11623        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
11624       (global.anim_status      == GAME_MODE_PSEUDO_NAMESONLY &&
11625        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
11626       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAMES &&
11627        global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
11628     global.anim_status = global.anim_status_next;
11629 }
11630
11631 void SetGameStatus(int game_status_new)
11632 {
11633   if (game_status_new != game_status)
11634     game_status_last_screen = game_status;
11635
11636   game_status = game_status_new;
11637
11638   SetAnimStatus(game_status_new);
11639 }
11640
11641 void SetFontStatus(int game_status_new)
11642 {
11643   static int last_game_status = -1;
11644
11645   if (game_status_new != -1)
11646   {
11647     // set game status for font use after storing last game status
11648     last_game_status = game_status;
11649     game_status = game_status_new;
11650   }
11651   else
11652   {
11653     // reset game status after font use from last stored game status
11654     game_status = last_game_status;
11655   }
11656 }
11657
11658 void ResetFontStatus(void)
11659 {
11660   SetFontStatus(-1);
11661 }
11662
11663 void SetLevelSetInfo(char *identifier, int level_nr)
11664 {
11665   setString(&levelset.identifier, identifier);
11666
11667   levelset.level_nr = level_nr;
11668 }
11669
11670 boolean CheckIfAllViewportsHaveChanged(void)
11671 {
11672   // if game status has not changed, viewports have not changed either
11673   if (game_status == game_status_last)
11674     return FALSE;
11675
11676   // check if all viewports have changed with current game status
11677
11678   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
11679   struct RectWithBorder *vp_door_1    = &viewport.door_1[game_status];
11680   struct RectWithBorder *vp_door_2    = &viewport.door_2[game_status];
11681   int new_real_sx       = vp_playfield->x;
11682   int new_real_sy       = vp_playfield->y;
11683   int new_full_sxsize   = vp_playfield->width;
11684   int new_full_sysize   = vp_playfield->height;
11685   int new_dx            = vp_door_1->x;
11686   int new_dy            = vp_door_1->y;
11687   int new_dxsize        = vp_door_1->width;
11688   int new_dysize        = vp_door_1->height;
11689   int new_vx            = vp_door_2->x;
11690   int new_vy            = vp_door_2->y;
11691   int new_vxsize        = vp_door_2->width;
11692   int new_vysize        = vp_door_2->height;
11693
11694   boolean playfield_viewport_has_changed =
11695     (new_real_sx != REAL_SX ||
11696      new_real_sy != REAL_SY ||
11697      new_full_sxsize != FULL_SXSIZE ||
11698      new_full_sysize != FULL_SYSIZE);
11699
11700   boolean door_1_viewport_has_changed =
11701     (new_dx != DX ||
11702      new_dy != DY ||
11703      new_dxsize != DXSIZE ||
11704      new_dysize != DYSIZE);
11705
11706   boolean door_2_viewport_has_changed =
11707     (new_vx != VX ||
11708      new_vy != VY ||
11709      new_vxsize != VXSIZE ||
11710      new_vysize != VYSIZE ||
11711      game_status_last == GAME_MODE_EDITOR);
11712
11713   return (playfield_viewport_has_changed &&
11714           door_1_viewport_has_changed &&
11715           door_2_viewport_has_changed);
11716 }
11717
11718 boolean CheckFadeAll(void)
11719 {
11720   return (CheckIfGlobalBorderHasChanged() ||
11721           CheckIfAllViewportsHaveChanged());
11722 }
11723
11724 void ChangeViewportPropertiesIfNeeded(void)
11725 {
11726   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
11727                                FALSE : setup.small_game_graphics);
11728   int gfx_game_mode = getGlobalGameStatus(game_status);
11729   int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
11730                         gfx_game_mode);
11731   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
11732   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
11733   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
11734   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
11735   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
11736   int new_win_xsize     = vp_window->width;
11737   int new_win_ysize     = vp_window->height;
11738   int border_left       = vp_playfield->border_left;
11739   int border_right      = vp_playfield->border_right;
11740   int border_top        = vp_playfield->border_top;
11741   int border_bottom     = vp_playfield->border_bottom;
11742   int new_sx            = vp_playfield->x      + border_left;
11743   int new_sy            = vp_playfield->y      + border_top;
11744   int new_sxsize        = vp_playfield->width  - border_left - border_right;
11745   int new_sysize        = vp_playfield->height - border_top  - border_bottom;
11746   int new_real_sx       = vp_playfield->x;
11747   int new_real_sy       = vp_playfield->y;
11748   int new_full_sxsize   = vp_playfield->width;
11749   int new_full_sysize   = vp_playfield->height;
11750   int new_dx            = vp_door_1->x;
11751   int new_dy            = vp_door_1->y;
11752   int new_dxsize        = vp_door_1->width;
11753   int new_dysize        = vp_door_1->height;
11754   int new_vx            = vp_door_2->x;
11755   int new_vy            = vp_door_2->y;
11756   int new_vxsize        = vp_door_2->width;
11757   int new_vysize        = vp_door_2->height;
11758   int new_ex            = vp_door_3->x;
11759   int new_ey            = vp_door_3->y;
11760   int new_exsize        = vp_door_3->width;
11761   int new_eysize        = vp_door_3->height;
11762   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
11763   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
11764                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
11765   int new_scr_fieldx = new_sxsize / tilesize;
11766   int new_scr_fieldy = new_sysize / tilesize;
11767   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
11768   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
11769   boolean init_gfx_buffers = FALSE;
11770   boolean init_video_buffer = FALSE;
11771   boolean init_gadgets_and_anims = FALSE;
11772   boolean init_bd_graphics = FALSE;
11773   boolean init_em_graphics = FALSE;
11774
11775   if (new_win_xsize != WIN_XSIZE ||
11776       new_win_ysize != WIN_YSIZE)
11777   {
11778     WIN_XSIZE = new_win_xsize;
11779     WIN_YSIZE = new_win_ysize;
11780
11781     init_video_buffer = TRUE;
11782     init_gfx_buffers = TRUE;
11783     init_gadgets_and_anims = TRUE;
11784
11785     // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
11786   }
11787
11788   if (new_scr_fieldx != SCR_FIELDX ||
11789       new_scr_fieldy != SCR_FIELDY)
11790   {
11791     // this always toggles between MAIN and GAME when using small tile size
11792
11793     SCR_FIELDX = new_scr_fieldx;
11794     SCR_FIELDY = new_scr_fieldy;
11795
11796     // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
11797   }
11798
11799   if (new_sx != SX ||
11800       new_sy != SY ||
11801       new_dx != DX ||
11802       new_dy != DY ||
11803       new_vx != VX ||
11804       new_vy != VY ||
11805       new_ex != EX ||
11806       new_ey != EY ||
11807       new_sxsize != SXSIZE ||
11808       new_sysize != SYSIZE ||
11809       new_dxsize != DXSIZE ||
11810       new_dysize != DYSIZE ||
11811       new_vxsize != VXSIZE ||
11812       new_vysize != VYSIZE ||
11813       new_exsize != EXSIZE ||
11814       new_eysize != EYSIZE ||
11815       new_real_sx != REAL_SX ||
11816       new_real_sy != REAL_SY ||
11817       new_full_sxsize != FULL_SXSIZE ||
11818       new_full_sysize != FULL_SYSIZE ||
11819       new_tilesize_var != TILESIZE_VAR
11820       )
11821   {
11822     // ------------------------------------------------------------------------
11823     // determine next fading area for changed viewport definitions
11824     // ------------------------------------------------------------------------
11825
11826     // start with current playfield area (default fading area)
11827     FADE_SX = REAL_SX;
11828     FADE_SY = REAL_SY;
11829     FADE_SXSIZE = FULL_SXSIZE;
11830     FADE_SYSIZE = FULL_SYSIZE;
11831
11832     // add new playfield area if position or size has changed
11833     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
11834         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
11835     {
11836       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11837                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
11838     }
11839
11840     // add current and new door 1 area if position or size has changed
11841     if (new_dx != DX || new_dy != DY ||
11842         new_dxsize != DXSIZE || new_dysize != DYSIZE)
11843     {
11844       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11845                      DX, DY, DXSIZE, DYSIZE);
11846       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11847                      new_dx, new_dy, new_dxsize, new_dysize);
11848     }
11849
11850     // add current and new door 2 area if position or size has changed
11851     if (new_vx != VX || new_vy != VY ||
11852         new_vxsize != VXSIZE || new_vysize != VYSIZE)
11853     {
11854       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11855                      VX, VY, VXSIZE, VYSIZE);
11856       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11857                      new_vx, new_vy, new_vxsize, new_vysize);
11858     }
11859
11860     // ------------------------------------------------------------------------
11861     // handle changed tile size
11862     // ------------------------------------------------------------------------
11863
11864     if (new_tilesize_var != TILESIZE_VAR)
11865     {
11866       // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
11867
11868       // changing tile size invalidates scroll values of engine snapshots
11869       FreeEngineSnapshotSingle();
11870
11871       // changing tile size requires update of graphic mapping for BD/EM engine
11872       init_bd_graphics = TRUE;
11873       init_em_graphics = TRUE;
11874     }
11875
11876     SX = new_sx;
11877     SY = new_sy;
11878     DX = new_dx;
11879     DY = new_dy;
11880     VX = new_vx;
11881     VY = new_vy;
11882     EX = new_ex;
11883     EY = new_ey;
11884     SXSIZE = new_sxsize;
11885     SYSIZE = new_sysize;
11886     DXSIZE = new_dxsize;
11887     DYSIZE = new_dysize;
11888     VXSIZE = new_vxsize;
11889     VYSIZE = new_vysize;
11890     EXSIZE = new_exsize;
11891     EYSIZE = new_eysize;
11892     REAL_SX = new_real_sx;
11893     REAL_SY = new_real_sy;
11894     FULL_SXSIZE = new_full_sxsize;
11895     FULL_SYSIZE = new_full_sysize;
11896     TILESIZE_VAR = new_tilesize_var;
11897
11898     init_gfx_buffers = TRUE;
11899     init_gadgets_and_anims = TRUE;
11900
11901     // Debug("tools:viewport", "viewports: init_gfx_buffers");
11902     // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
11903   }
11904
11905   if (init_gfx_buffers)
11906   {
11907     // Debug("tools:viewport", "init_gfx_buffers");
11908
11909     SCR_FIELDX = new_scr_fieldx_buffers;
11910     SCR_FIELDY = new_scr_fieldy_buffers;
11911
11912     InitGfxBuffers();
11913
11914     SCR_FIELDX = new_scr_fieldx;
11915     SCR_FIELDY = new_scr_fieldy;
11916
11917     SetDrawDeactivationMask(REDRAW_NONE);
11918     SetDrawBackgroundMask(REDRAW_FIELD);
11919   }
11920
11921   if (init_video_buffer)
11922   {
11923     // Debug("tools:viewport", "init_video_buffer");
11924
11925     FreeAllImageTextures();     // needs old renderer to free the textures
11926
11927     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
11928     InitImageTextures();
11929   }
11930
11931   if (init_gadgets_and_anims)
11932   {
11933     // Debug("tools:viewport", "init_gadgets_and_anims");
11934
11935     InitGadgets();
11936     InitGlobalAnimations();
11937   }
11938
11939   if (init_bd_graphics)
11940   {
11941     InitGraphicInfo_BD();
11942   }
11943
11944   if (init_em_graphics)
11945   {
11946     InitGraphicInfo_EM();
11947   }
11948 }
11949
11950 void OpenURL(char *url)
11951 {
11952 #if SDL_VERSION_ATLEAST(2,0,14)
11953   SDL_OpenURL(url);
11954 #else
11955   Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
11956        url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
11957   Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
11958 #endif
11959 }
11960
11961 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
11962 {
11963   OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
11964 }
11965
11966
11967 // ============================================================================
11968 // tests
11969 // ============================================================================
11970
11971 #if defined(PLATFORM_WINDOWS)
11972 /* FILETIME of Jan 1 1970 00:00:00. */
11973 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
11974
11975 /*
11976  * timezone information is stored outside the kernel so tzp isn't used anymore.
11977  *
11978  * Note: this function is not for Win32 high precision timing purpose. See
11979  * elapsed_time().
11980  */
11981 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
11982 {
11983   FILETIME    file_time;
11984   SYSTEMTIME  system_time;
11985   ULARGE_INTEGER ularge;
11986
11987   GetSystemTime(&system_time);
11988   SystemTimeToFileTime(&system_time, &file_time);
11989   ularge.LowPart = file_time.dwLowDateTime;
11990   ularge.HighPart = file_time.dwHighDateTime;
11991
11992   tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
11993   tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
11994
11995   return 0;
11996 }
11997 #endif
11998
11999 static char *test_init_uuid_random_function_simple(void)
12000 {
12001   static char seed_text[100];
12002   unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
12003
12004   sprintf(seed_text, "%d", seed);
12005
12006   return seed_text;
12007 }
12008
12009 static char *test_init_uuid_random_function_better(void)
12010 {
12011   static char seed_text[100];
12012   struct timeval current_time;
12013
12014   gettimeofday(&current_time, NULL);
12015
12016   prng_seed_bytes(&current_time, sizeof(current_time));
12017
12018   sprintf(seed_text, "%ld.%ld",
12019           (long)current_time.tv_sec,
12020           (long)current_time.tv_usec);
12021
12022   return seed_text;
12023 }
12024
12025 #if defined(PLATFORM_WINDOWS)
12026 static char *test_init_uuid_random_function_better_windows(void)
12027 {
12028   static char seed_text[100];
12029   struct timeval current_time;
12030
12031   gettimeofday_windows(&current_time, NULL);
12032
12033   prng_seed_bytes(&current_time, sizeof(current_time));
12034
12035   sprintf(seed_text, "%ld.%ld",
12036           (long)current_time.tv_sec,
12037           (long)current_time.tv_usec);
12038
12039   return seed_text;
12040 }
12041 #endif
12042
12043 static unsigned int test_uuid_random_function_simple(int max)
12044 {
12045   return GetSimpleRandom(max);
12046 }
12047
12048 static unsigned int test_uuid_random_function_better(int max)
12049 {
12050   return (max > 0 ? prng_get_uint() % max : 0);
12051 }
12052
12053 #if defined(PLATFORM_WINDOWS)
12054 #define NUM_UUID_TESTS                  3
12055 #else
12056 #define NUM_UUID_TESTS                  2
12057 #endif
12058
12059 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
12060 {
12061   HashTable *hash_seeds =
12062     create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
12063   HashTable *hash_uuids =
12064     create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
12065   static char message[100];
12066   int i;
12067
12068   char *random_name = (nr == 0 ? "simple" : "better");
12069   char *random_type = (always_seed ? "always" : "only once");
12070   char *(*init_random_function)(void) =
12071     (nr == 0 ?
12072      test_init_uuid_random_function_simple :
12073      test_init_uuid_random_function_better);
12074   unsigned int (*random_function)(int) =
12075     (nr == 0 ?
12076      test_uuid_random_function_simple :
12077      test_uuid_random_function_better);
12078   int xpos = 40;
12079
12080 #if defined(PLATFORM_WINDOWS)
12081   if (nr == 2)
12082   {
12083     random_name = "windows";
12084     init_random_function = test_init_uuid_random_function_better_windows;
12085   }
12086 #endif
12087
12088   ClearField();
12089
12090   DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
12091   DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
12092
12093   DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
12094   DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
12095   DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
12096
12097   DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
12098
12099   BackToFront();
12100
12101   // always initialize random number generator at least once
12102   init_random_function();
12103
12104   unsigned int time_start = SDL_GetTicks();
12105
12106   for (i = 0; i < num_uuids; i++)
12107   {
12108     if (always_seed)
12109     {
12110       char *seed = getStringCopy(init_random_function());
12111
12112       hashtable_remove(hash_seeds, seed);
12113       hashtable_insert(hash_seeds, seed, "1");
12114     }
12115
12116     char *uuid = getStringCopy(getUUIDExt(random_function));
12117
12118     hashtable_remove(hash_uuids, uuid);
12119     hashtable_insert(hash_uuids, uuid, "1");
12120   }
12121
12122   int num_unique_seeds = hashtable_count(hash_seeds);
12123   int num_unique_uuids = hashtable_count(hash_uuids);
12124
12125   unsigned int time_needed = SDL_GetTicks() - time_start;
12126
12127   DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
12128
12129   DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
12130
12131   if (always_seed)
12132     DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
12133
12134   if (nr == NUM_UUID_TESTS - 1 && always_seed)
12135     DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
12136   else
12137     DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
12138
12139   sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
12140
12141   Request(message, REQ_CONFIRM);
12142
12143   hashtable_destroy(hash_seeds);
12144   hashtable_destroy(hash_uuids);
12145 }
12146
12147 void TestGeneratingUUIDs(void)
12148 {
12149   int num_uuids = 1000000;
12150   int i, j;
12151
12152   for (i = 0; i < NUM_UUID_TESTS; i++)
12153     for (j = 0; j < 2; j++)
12154       TestGeneratingUUIDs_RunTest(i, j, num_uuids);
12155
12156   CloseAllAndExit(0);
12157 }