c271314e406df9222193be7e68343322a07014f3
[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 = el2edimg(element);
2822
2823     if (masked)
2824       DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2825     else
2826       DrawSizedGraphic(x, y, graphic, 0, tilesize);
2827   }
2828 }
2829
2830 void DrawSizedElement(int x, int y, int element, int tilesize)
2831 {
2832   DrawSizedElementExt(x, y, element, tilesize, FALSE);
2833 }
2834
2835 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2836 {
2837   DrawSizedElementExt(x, y, element, tilesize, TRUE);
2838 }
2839
2840 void DrawMiniElement(int x, int y, int element)
2841 {
2842   int graphic;
2843
2844   graphic = el2edimg(element);
2845   DrawMiniGraphic(x, y, graphic);
2846 }
2847
2848 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2849                             int tilesize)
2850 {
2851   int x = sx + scroll_x, y = sy + scroll_y;
2852
2853   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2854     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2855   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2856     DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2857   else
2858     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2859 }
2860
2861 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2862 {
2863   int x = sx + scroll_x, y = sy + scroll_y;
2864
2865   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2866     DrawMiniElement(sx, sy, EL_EMPTY);
2867   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2868     DrawMiniElement(sx, sy, Tile[x][y]);
2869   else
2870     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2871 }
2872
2873 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2874                                         int x, int y, int xsize, int ysize,
2875                                         int tile_width, int tile_height)
2876 {
2877   Bitmap *src_bitmap;
2878   int src_x, src_y;
2879   int dst_x = startx + x * tile_width;
2880   int dst_y = starty + y * tile_height;
2881   int width  = graphic_info[graphic].width;
2882   int height = graphic_info[graphic].height;
2883   int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
2884   int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2885   int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
2886   int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2887   int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
2888   int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2889   boolean draw_masked = graphic_info[graphic].draw_masked;
2890
2891   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2892
2893   if (src_bitmap == NULL || width < tile_width || height < tile_height)
2894   {
2895     ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2896     return;
2897   }
2898
2899   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
2900             inner_sx + (x - 1) * tile_width  % inner_width);
2901   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2902             inner_sy + (y - 1) * tile_height % inner_height);
2903
2904   if (draw_masked)
2905     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2906                      dst_x, dst_y);
2907   else
2908     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2909                dst_x, dst_y);
2910 }
2911
2912 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2913                                    int x, int y, int xsize, int ysize,
2914                                    int font_nr)
2915 {
2916   int font_width  = getFontWidth(font_nr);
2917   int font_height = getFontHeight(font_nr);
2918
2919   DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2920                               font_width, font_height);
2921 }
2922
2923 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2924 {
2925   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2926   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2927   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2928   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2929   boolean no_delay = (tape.warp_forward);
2930   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2931   int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2932   DelayCounter anim_delay = { anim_delay_value };
2933   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2934   int font_width = getFontWidth(font_nr);
2935   int font_height = getFontHeight(font_nr);
2936   int max_xsize = level.envelope[envelope_nr].xsize;
2937   int max_ysize = level.envelope[envelope_nr].ysize;
2938   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2939   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2940   int xend = max_xsize;
2941   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2942   int xstep = (xstart < xend ? 1 : 0);
2943   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2944   int start = 0;
2945   int end = MAX(xend - xstart, yend - ystart);
2946   int i;
2947
2948   for (i = start; i <= end; i++)
2949   {
2950     int last_frame = end;       // last frame of this "for" loop
2951     int x = xstart + i * xstep;
2952     int y = ystart + i * ystep;
2953     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2954     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2955     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2956     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2957     int xx, yy;
2958
2959     SetDrawtoField(DRAW_TO_FIELDBUFFER);
2960
2961     BlitScreenToBitmap(backbuffer);
2962
2963     SetDrawtoField(DRAW_TO_BACKBUFFER);
2964
2965     for (yy = 0; yy < ysize; yy++)
2966       for (xx = 0; xx < xsize; xx++)
2967         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2968
2969     DrawTextArea(sx + font_width, sy + font_height,
2970                  level.envelope[envelope_nr].text, font_nr, max_xsize,
2971                  xsize - 2, ysize - 2, 0, mask_mode,
2972                  level.envelope[envelope_nr].autowrap,
2973                  level.envelope[envelope_nr].centered, FALSE);
2974
2975     redraw_mask |= REDRAW_FIELD;
2976     BackToFront();
2977
2978     SkipUntilDelayReached(&anim_delay, &i, last_frame);
2979   }
2980
2981   ClearAutoRepeatKeyEvents();
2982 }
2983
2984 void ShowEnvelope(int envelope_nr)
2985 {
2986   int element = EL_ENVELOPE_1 + envelope_nr;
2987   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2988   int sound_opening = element_info[element].sound[ACTION_OPENING];
2989   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2990   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2991   boolean no_delay = (tape.warp_forward);
2992   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2993   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2994   int anim_mode = graphic_info[graphic].anim_mode;
2995   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2996                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2997   boolean overlay_enabled = GetOverlayEnabled();
2998
2999   game.envelope_active = TRUE;  // needed for RedrawPlayfield() events
3000
3001   SetOverlayEnabled(FALSE);
3002   UnmapAllGadgets();
3003
3004   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3005
3006   if (anim_mode == ANIM_DEFAULT)
3007     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
3008
3009   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
3010
3011   if (tape.playing)
3012     Delay_WithScreenUpdates(wait_delay_value);
3013   else
3014     WaitForEventToContinue();
3015
3016   RemapAllGadgets();
3017   SetOverlayEnabled(overlay_enabled);
3018
3019   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3020
3021   if (anim_mode != ANIM_NONE)
3022     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3023
3024   if (anim_mode == ANIM_DEFAULT)
3025     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3026
3027   game.envelope_active = FALSE;
3028
3029   SetDrawtoField(DRAW_TO_FIELDBUFFER);
3030
3031   redraw_mask |= REDRAW_FIELD;
3032   BackToFront();
3033 }
3034
3035 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3036                                            int xsize, int ysize)
3037 {
3038   if (!global.use_envelope_request)
3039     return;
3040
3041   if (request.bitmap == NULL ||
3042       xsize > request.xsize ||
3043       ysize > request.ysize)
3044   {
3045     if (request.bitmap != NULL)
3046       FreeBitmap(request.bitmap);
3047
3048     request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3049
3050     SDL_Surface *surface = request.bitmap->surface;
3051
3052     if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3053       Fail("SDLGetNativeSurface() failed");
3054   }
3055
3056   BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3057
3058   // create masked surface for request bitmap, if needed
3059   if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3060   {
3061     SDL_Surface *surface        = request.bitmap->surface;
3062     SDL_Surface *surface_masked = request.bitmap->surface_masked;
3063
3064     SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3065     SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3066                     SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3067   }
3068
3069   SDLFreeBitmapTextures(request.bitmap);
3070   SDLCreateBitmapTextures(request.bitmap);
3071
3072   ResetBitmapAlpha(request.bitmap);
3073
3074   // set envelope request run-time values
3075   request.sx = sx;
3076   request.sy = sy;
3077   request.xsize = xsize;
3078   request.ysize = ysize;
3079 }
3080
3081 void DrawEnvelopeRequestToScreen(int drawing_target)
3082 {
3083   if (global.use_envelope_request &&
3084       game.request_active &&
3085       drawing_target == DRAW_TO_SCREEN)
3086   {
3087     struct GraphicInfo *g = &graphic_info[IMG_BACKGROUND_REQUEST];
3088
3089     SetBitmapAlphaNextBlit(request.bitmap, g->alpha);
3090
3091     if (g->draw_masked)
3092       BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3093                          request.sx, request.sy);
3094     else
3095       BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3096                    request.sx, request.sy);
3097   }
3098 }
3099
3100 static void setRequestBasePosition(int *x, int *y)
3101 {
3102   int sx_base, sy_base;
3103
3104   if (request.x != -1)
3105     sx_base = request.x;
3106   else if (request.align == ALIGN_LEFT)
3107     sx_base = SX;
3108   else if (request.align == ALIGN_RIGHT)
3109     sx_base = SX + SXSIZE;
3110   else
3111     sx_base = SX + SXSIZE / 2;
3112
3113   if (request.y != -1)
3114     sy_base = request.y;
3115   else if (request.valign == VALIGN_TOP)
3116     sy_base = SY;
3117   else if (request.valign == VALIGN_BOTTOM)
3118     sy_base = SY + SYSIZE;
3119   else
3120     sy_base = SY + SYSIZE / 2;
3121
3122   *x = sx_base;
3123   *y = sy_base;
3124 }
3125
3126 static void setRequestPositionExt(int *x, int *y, int width, int height,
3127                                   boolean add_border_size)
3128 {
3129   int border_size = request.border_size;
3130   int sx_base, sy_base;
3131   int sx, sy;
3132
3133   setRequestBasePosition(&sx_base, &sy_base);
3134
3135   if (request.align == ALIGN_LEFT)
3136     sx = sx_base;
3137   else if (request.align == ALIGN_RIGHT)
3138     sx = sx_base - width;
3139   else
3140     sx = sx_base - width  / 2;
3141
3142   if (request.valign == VALIGN_TOP)
3143     sy = sy_base;
3144   else if (request.valign == VALIGN_BOTTOM)
3145     sy = sy_base - height;
3146   else
3147     sy = sy_base - height / 2;
3148
3149   sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3150   sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3151
3152   if (add_border_size)
3153   {
3154     sx += border_size;
3155     sy += border_size;
3156   }
3157
3158   *x = sx;
3159   *y = sy;
3160 }
3161
3162 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3163 {
3164   setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3165 }
3166
3167 static void DrawEnvelopeRequestText(int sx, int sy, char *text)
3168 {
3169   char *text_final = text;
3170   char *text_door_style = NULL;
3171   int graphic = IMG_BACKGROUND_REQUEST;
3172   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3173   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3174   int font_nr = FONT_REQUEST;
3175   int font_width = getFontWidth(font_nr);
3176   int font_height = getFontHeight(font_nr);
3177   int border_size = request.border_size;
3178   int line_spacing = request.line_spacing;
3179   int line_height = font_height + line_spacing;
3180   int max_text_width  = request.width  - 2 * border_size;
3181   int max_text_height = request.height - 2 * border_size;
3182   int line_length = max_text_width  / font_width;
3183   int max_lines   = max_text_height / line_height;
3184   int text_width = line_length * font_width;
3185   int sx_offset = border_size;
3186   int sy_offset = border_size;
3187
3188   // force DOOR font inside door area
3189   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3190
3191   if (request.centered)
3192     sx_offset = (request.width - text_width) / 2;
3193
3194   if (request.wrap_single_words && !request.autowrap)
3195   {
3196     char *src_text_ptr, *dst_text_ptr;
3197
3198     if (maxWordLengthInRequestString(text) > line_length)
3199     {
3200       font_nr = FONT_REQUEST_NARROW;
3201       font_width = getFontWidth(font_nr);
3202       line_length = max_text_width  / font_width;
3203     }
3204
3205     text_door_style = checked_malloc(2 * strlen(text) + 1);
3206
3207     src_text_ptr = text;
3208     dst_text_ptr = text_door_style;
3209
3210     while (*src_text_ptr)
3211     {
3212       if (*src_text_ptr == ' ' ||
3213           *src_text_ptr == '?' ||
3214           *src_text_ptr == '!')
3215         *dst_text_ptr++ = '\n';
3216
3217       if (*src_text_ptr != ' ')
3218         *dst_text_ptr++ = *src_text_ptr;
3219
3220       src_text_ptr++;
3221     }
3222
3223     *dst_text_ptr = '\0';
3224
3225     text_final = text_door_style;
3226   }
3227
3228   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3229                  line_length, -1, max_lines, line_spacing, mask_mode,
3230                  request.autowrap, request.centered, FALSE);
3231
3232   if (text_door_style)
3233     free(text_door_style);
3234
3235   ResetFontStatus();
3236 }
3237
3238 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3239 {
3240   DrawBuffer *drawto_last = drawto;
3241   int graphic = IMG_BACKGROUND_REQUEST;
3242   int width = request.width;
3243   int height = request.height;
3244   int tile_size = MAX(request.step_offset, 1);
3245   int x_steps = width  / tile_size;
3246   int y_steps = height / tile_size;
3247   int sx, sy;
3248   int x, y;
3249
3250   setRequestPosition(&sx, &sy, FALSE);
3251
3252   // draw complete envelope request to temporary bitmap
3253   drawto = bitmap_db_store_1;
3254
3255   ClearRectangle(drawto, sx, sy, width, height);
3256
3257   for (y = 0; y < y_steps; y++)
3258     for (x = 0; x < x_steps; x++)
3259       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3260                                   x, y, x_steps, y_steps,
3261                                   tile_size, tile_size);
3262
3263   // write text for request
3264   DrawEnvelopeRequestText(sx, sy, text);
3265
3266   MapToolButtons(req_state);
3267
3268   // restore pointer to drawing buffer
3269   drawto = drawto_last;
3270
3271   // prepare complete envelope request from temporary bitmap
3272   PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3273 }
3274
3275 static void AnimateEnvelopeRequest(int anim_mode, int action)
3276 {
3277   boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3278   int delay_value_normal = request.step_delay;
3279   int delay_value_fast = delay_value_normal / 2;
3280   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3281   boolean no_delay = (tape.warp_forward);
3282   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3283   int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3284   DelayCounter anim_delay = { anim_delay_value };
3285
3286   int tile_size = MAX(request.step_offset, 1);
3287   int max_xsize = request.width  / tile_size;
3288   int max_ysize = request.height / tile_size;
3289   int max_xsize_inner = max_xsize - 2;
3290   int max_ysize_inner = max_ysize - 2;
3291
3292   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3293   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3294   int xend = max_xsize_inner;
3295   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3296   int xstep = (xstart < xend ? 1 : 0);
3297   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3298   int start = 0;
3299   int end = MAX(xend - xstart, yend - ystart);
3300   int i;
3301
3302   if (setup.quick_doors)
3303   {
3304     xstart = xend;
3305     ystart = yend;
3306     end = 0;
3307   }
3308
3309   for (i = start; i <= end; i++)
3310   {
3311     int last_frame = end;       // last frame of this "for" loop
3312     int x = xstart + i * xstep;
3313     int y = ystart + i * ystep;
3314     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3315     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3316     int xsize_size_left = (xsize - 1) * tile_size;
3317     int ysize_size_top  = (ysize - 1) * tile_size;
3318     int max_xsize_pos = (max_xsize - 1) * tile_size;
3319     int max_ysize_pos = (max_ysize - 1) * tile_size;
3320     int width  = xsize * tile_size;
3321     int height = ysize * tile_size;
3322     int src_x, src_y;
3323     int dst_x, dst_y;
3324     int xx, yy;
3325
3326     if (game_ended)
3327       HandleGameActions();
3328
3329     setRequestPosition(&src_x, &src_y, FALSE);
3330     setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3331
3332     for (yy = 0; yy < 2; yy++)
3333     {
3334       for (xx = 0; xx < 2; xx++)
3335       {
3336         int src_xx = src_x + xx * max_xsize_pos;
3337         int src_yy = src_y + yy * max_ysize_pos;
3338         int dst_xx = dst_x + xx * xsize_size_left;
3339         int dst_yy = dst_y + yy * ysize_size_top;
3340         int xx_size = (xx ? tile_size : xsize_size_left);
3341         int yy_size = (yy ? tile_size : ysize_size_top);
3342
3343         // draw partial (animated) envelope request to temporary bitmap
3344         BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3345                    src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3346       }
3347     }
3348
3349     // prepare partial (animated) envelope request from temporary bitmap
3350     PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3351                                    width, height);
3352
3353     redraw_mask |= REDRAW_FIELD;
3354
3355     BackToFront();
3356
3357     SkipUntilDelayReached(&anim_delay, &i, last_frame);
3358   }
3359
3360   ClearAutoRepeatKeyEvents();
3361 }
3362
3363 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3364 {
3365   int graphic = IMG_BACKGROUND_REQUEST;
3366   int sound_opening = SND_REQUEST_OPENING;
3367   int sound_closing = SND_REQUEST_CLOSING;
3368   int anim_mode_1 = request.anim_mode;                  // (higher priority)
3369   int anim_mode_2 = graphic_info[graphic].anim_mode;    // (lower priority)
3370   int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3371   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3372                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3373
3374   game.envelope_active = TRUE;  // needed for RedrawPlayfield() events
3375
3376   if (action == ACTION_OPENING)
3377   {
3378     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3379
3380     if (anim_mode == ANIM_DEFAULT)
3381       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3382
3383     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3384   }
3385   else
3386   {
3387     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3388
3389     if (anim_mode != ANIM_NONE)
3390       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3391
3392     if (anim_mode == ANIM_DEFAULT)
3393       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3394   }
3395
3396   game.envelope_active = FALSE;
3397 }
3398
3399 static Bitmap *GetPreviewTileBitmap(Bitmap *bitmap)
3400 {
3401   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
3402     return GetPreviewTileBitmap_BD(bitmap);
3403
3404   return bitmap;
3405 }
3406
3407 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3408 {
3409   if (IS_MM_WALL(element))
3410   {
3411     DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3412   }
3413   else
3414   {
3415     Bitmap *src_bitmap;
3416     int src_x, src_y;
3417     int graphic = el2preimg(element);
3418
3419     getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3420
3421     // for BD style levels, maybe use bitmap with level-specific colors
3422     src_bitmap = GetPreviewTileBitmap(src_bitmap);
3423
3424     BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3425                dst_x, dst_y);
3426   }
3427 }
3428
3429 void DrawLevel(int draw_background_mask)
3430 {
3431   int x, y;
3432
3433   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3434   SetDrawBackgroundMask(draw_background_mask);
3435
3436   ClearField();
3437
3438   for (x = BX1; x <= BX2; x++)
3439     for (y = BY1; y <= BY2; y++)
3440       DrawScreenField(x, y);
3441
3442   redraw_mask |= REDRAW_FIELD;
3443 }
3444
3445 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3446                     int tilesize)
3447 {
3448   int x, y;
3449
3450   for (x = 0; x < size_x; x++)
3451     for (y = 0; y < size_y; y++)
3452       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3453
3454   redraw_mask |= REDRAW_FIELD;
3455 }
3456
3457 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3458 {
3459   int x, y;
3460
3461   for (x = 0; x < size_x; x++)
3462     for (y = 0; y < size_y; y++)
3463       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3464
3465   redraw_mask |= REDRAW_FIELD;
3466 }
3467
3468 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3469 {
3470   boolean show_level_border = (BorderElement != EL_EMPTY);
3471   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3472   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3473   int tile_size = preview.tile_size;
3474   int preview_width  = preview.xsize * tile_size;
3475   int preview_height = preview.ysize * tile_size;
3476   int real_preview_xsize = MIN(level_xsize, preview.xsize);
3477   int real_preview_ysize = MIN(level_ysize, preview.ysize);
3478   int real_preview_width  = real_preview_xsize * tile_size;
3479   int real_preview_height = real_preview_ysize * tile_size;
3480   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3481   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3482   int x, y;
3483
3484   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3485     return;
3486
3487   DrawBackground(dst_x, dst_y, preview_width, preview_height);
3488
3489   dst_x += (preview_width  - real_preview_width)  / 2;
3490   dst_y += (preview_height - real_preview_height) / 2;
3491
3492   for (x = 0; x < real_preview_xsize; x++)
3493   {
3494     for (y = 0; y < real_preview_ysize; y++)
3495     {
3496       int lx = from_x + x + (show_level_border ? -1 : 0);
3497       int ly = from_y + y + (show_level_border ? -1 : 0);
3498       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3499                      getBorderElement(lx, ly));
3500
3501       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3502                          element, tile_size);
3503     }
3504   }
3505
3506   redraw_mask |= REDRAW_FIELD;
3507 }
3508
3509 #define MICROLABEL_EMPTY                0
3510 #define MICROLABEL_LEVEL_NAME           1
3511 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
3512 #define MICROLABEL_LEVEL_AUTHOR         3
3513 #define MICROLABEL_IMPORTED_FROM_HEAD   4
3514 #define MICROLABEL_IMPORTED_FROM        5
3515 #define MICROLABEL_IMPORTED_BY_HEAD     6
3516 #define MICROLABEL_IMPORTED_BY          7
3517
3518 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3519 {
3520   int max_text_width = SXSIZE;
3521   int font_width = getFontWidth(font_nr);
3522
3523   if (pos->align == ALIGN_CENTER)
3524     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3525   else if (pos->align == ALIGN_RIGHT)
3526     max_text_width = pos->x;
3527   else
3528     max_text_width = SXSIZE - pos->x;
3529
3530   return max_text_width / font_width;
3531 }
3532
3533 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3534 {
3535   char label_text[MAX_OUTPUT_LINESIZE + 1];
3536   int max_len_label_text;
3537   int font_nr = pos->font;
3538   int i;
3539
3540   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3541     return;
3542
3543   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3544       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3545       mode == MICROLABEL_IMPORTED_BY_HEAD)
3546     font_nr = pos->font_alt;
3547
3548   max_len_label_text = getMaxTextLength(pos, font_nr);
3549
3550   if (pos->size != -1)
3551     max_len_label_text = pos->size;
3552
3553   for (i = 0; i < max_len_label_text; i++)
3554     label_text[i] = ' ';
3555   label_text[max_len_label_text] = '\0';
3556
3557   if (strlen(label_text) > 0)
3558     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3559
3560   strncpy(label_text,
3561           (mode == MICROLABEL_LEVEL_NAME ? level.name :
3562            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3563            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3564            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3565            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3566            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3567            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3568           max_len_label_text);
3569   label_text[max_len_label_text] = '\0';
3570
3571   if (strlen(label_text) > 0)
3572     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3573
3574   redraw_mask |= REDRAW_FIELD;
3575 }
3576
3577 static void DrawPreviewLevelLabel(int mode)
3578 {
3579   DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3580 }
3581
3582 static void DrawPreviewLevelInfo(int mode)
3583 {
3584   if (mode == MICROLABEL_LEVEL_NAME)
3585     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3586   else if (mode == MICROLABEL_LEVEL_AUTHOR)
3587     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3588 }
3589
3590 static void DrawPreviewLevelExt(boolean restart)
3591 {
3592   static DelayCounter scroll_delay = { 0 };
3593   static DelayCounter label_delay = { 0 };
3594   static int from_x, from_y, scroll_direction;
3595   static int label_state, label_counter;
3596   boolean show_level_border = (BorderElement != EL_EMPTY);
3597   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3598   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3599
3600   scroll_delay.value = preview.step_delay;
3601   label_delay.value = MICROLEVEL_LABEL_DELAY;
3602
3603   if (restart)
3604   {
3605     from_x = 0;
3606     from_y = 0;
3607
3608     if (preview.anim_mode == ANIM_CENTERED)
3609     {
3610       if (level_xsize > preview.xsize)
3611         from_x = (level_xsize - preview.xsize) / 2;
3612       if (level_ysize > preview.ysize)
3613         from_y = (level_ysize - preview.ysize) / 2;
3614     }
3615
3616     from_x += preview.xoffset;
3617     from_y += preview.yoffset;
3618
3619     scroll_direction = MV_RIGHT;
3620     label_state = 1;
3621     label_counter = 0;
3622
3623     DrawPreviewLevelPlayfield(from_x, from_y);
3624     DrawPreviewLevelLabel(label_state);
3625
3626     DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3627     DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3628
3629     // initialize delay counters
3630     ResetDelayCounter(&scroll_delay);
3631     ResetDelayCounter(&label_delay);
3632
3633     if (leveldir_current->name)
3634     {
3635       struct TextPosInfo *pos = &menu.main.text.level_info_1;
3636       char label_text[MAX_OUTPUT_LINESIZE + 1];
3637       int font_nr = pos->font;
3638       int max_len_label_text = getMaxTextLength(pos, font_nr);
3639
3640       if (pos->size != -1)
3641         max_len_label_text = pos->size;
3642
3643       strncpy(label_text, leveldir_current->name, max_len_label_text);
3644       label_text[max_len_label_text] = '\0';
3645
3646       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3647         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3648     }
3649
3650     return;
3651   }
3652
3653   // scroll preview level, if needed
3654   if (preview.anim_mode != ANIM_NONE &&
3655       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3656       DelayReached(&scroll_delay))
3657   {
3658     switch (scroll_direction)
3659     {
3660       case MV_LEFT:
3661         if (from_x > 0)
3662         {
3663           from_x -= preview.step_offset;
3664           from_x = (from_x < 0 ? 0 : from_x);
3665         }
3666         else
3667           scroll_direction = MV_UP;
3668         break;
3669
3670       case MV_RIGHT:
3671         if (from_x < level_xsize - preview.xsize)
3672         {
3673           from_x += preview.step_offset;
3674           from_x = (from_x > level_xsize - preview.xsize ?
3675                     level_xsize - preview.xsize : from_x);
3676         }
3677         else
3678           scroll_direction = MV_DOWN;
3679         break;
3680
3681       case MV_UP:
3682         if (from_y > 0)
3683         {
3684           from_y -= preview.step_offset;
3685           from_y = (from_y < 0 ? 0 : from_y);
3686         }
3687         else
3688           scroll_direction = MV_RIGHT;
3689         break;
3690
3691       case MV_DOWN:
3692         if (from_y < level_ysize - preview.ysize)
3693         {
3694           from_y += preview.step_offset;
3695           from_y = (from_y > level_ysize - preview.ysize ?
3696                     level_ysize - preview.ysize : from_y);
3697         }
3698         else
3699           scroll_direction = MV_LEFT;
3700         break;
3701
3702       default:
3703         break;
3704     }
3705
3706     DrawPreviewLevelPlayfield(from_x, from_y);
3707   }
3708
3709   // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3710   // redraw micro level label, if needed
3711   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3712       !strEqual(level.author, ANONYMOUS_NAME) &&
3713       !strEqual(level.author, leveldir_current->name) &&
3714       DelayReached(&label_delay))
3715   {
3716     int max_label_counter = 23;
3717
3718     if (leveldir_current->imported_from != NULL &&
3719         strlen(leveldir_current->imported_from) > 0)
3720       max_label_counter += 14;
3721     if (leveldir_current->imported_by != NULL &&
3722         strlen(leveldir_current->imported_by) > 0)
3723       max_label_counter += 14;
3724
3725     label_counter = (label_counter + 1) % max_label_counter;
3726     label_state = (label_counter >= 0 && label_counter <= 7 ?
3727                    MICROLABEL_LEVEL_NAME :
3728                    label_counter >= 9 && label_counter <= 12 ?
3729                    MICROLABEL_LEVEL_AUTHOR_HEAD :
3730                    label_counter >= 14 && label_counter <= 21 ?
3731                    MICROLABEL_LEVEL_AUTHOR :
3732                    label_counter >= 23 && label_counter <= 26 ?
3733                    MICROLABEL_IMPORTED_FROM_HEAD :
3734                    label_counter >= 28 && label_counter <= 35 ?
3735                    MICROLABEL_IMPORTED_FROM :
3736                    label_counter >= 37 && label_counter <= 40 ?
3737                    MICROLABEL_IMPORTED_BY_HEAD :
3738                    label_counter >= 42 && label_counter <= 49 ?
3739                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3740
3741     if (leveldir_current->imported_from == NULL &&
3742         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3743          label_state == MICROLABEL_IMPORTED_FROM))
3744       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3745                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3746
3747     DrawPreviewLevelLabel(label_state);
3748   }
3749 }
3750
3751 void DrawPreviewPlayers(void)
3752 {
3753   if (game_status != GAME_MODE_MAIN)
3754     return;
3755
3756   // do not draw preview players if level preview redefined, but players aren't
3757   if (preview.redefined && !menu.main.preview_players.redefined)
3758     return;
3759
3760   boolean player_found[MAX_PLAYERS];
3761   int num_players = 0;
3762   int i, x, y;
3763
3764   for (i = 0; i < MAX_PLAYERS; i++)
3765     player_found[i] = FALSE;
3766
3767   // check which players can be found in the level (simple approach)
3768   for (x = 0; x < lev_fieldx; x++)
3769   {
3770     for (y = 0; y < lev_fieldy; y++)
3771     {
3772       int element = level.field[x][y];
3773
3774       if (IS_PLAYER_ELEMENT(element))
3775       {
3776         int player_nr = GET_PLAYER_NR(element);
3777
3778         player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3779
3780         if (!player_found[player_nr])
3781           num_players++;
3782
3783         player_found[player_nr] = TRUE;
3784       }
3785     }
3786   }
3787
3788   struct TextPosInfo *pos = &menu.main.preview_players;
3789   int tile_size = pos->tile_size;
3790   int border_size = pos->border_size;
3791   int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3792   int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3793   int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3794   int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3795   int max_players_width  = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3796   int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3797   int all_players_width  = (num_players - 1) * player_xoffset + tile_size;
3798   int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3799   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3800   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3801   int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width,  pos->align);
3802   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3803
3804   // clear area in which the players will be drawn
3805   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3806                              max_players_width, max_players_height);
3807
3808   if (!network.enabled && !setup.team_mode)
3809     return;
3810
3811   // only draw players if level is suited for team mode
3812   if (num_players < 2)
3813     return;
3814
3815   // draw all players that were found in the level
3816   for (i = 0; i < MAX_PLAYERS; i++)
3817   {
3818     if (player_found[i])
3819     {
3820       int graphic = el2img(EL_PLAYER_1 + i);
3821
3822       DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3823
3824       xpos += player_xoffset;
3825       ypos += player_yoffset;
3826     }
3827   }
3828 }
3829
3830 static void PreparePreviewTileBitmap(void)
3831 {
3832   // check if special preview bitmap with level-specific colors should be created
3833   if (level.game_engine_type != GAME_ENGINE_TYPE_BD)
3834     return;
3835
3836   // use original sized bitmap (else reduced color palette is lost by downscaling)
3837   int original_tilesize = MAX(MINI_TILESIZE, preview.tile_size);
3838   int scale_down_factor = original_tilesize / preview.tile_size;
3839   Bitmap *src_bitmap;
3840   int src_x, src_y;
3841   int element = EL_BD_ROCK;
3842   int graphic = el2preimg(element);
3843
3844   // create special preview bitmap and scale it down to preview tile size
3845   getSizedGraphicSource(graphic, 0, original_tilesize, &src_bitmap, &src_x, &src_y);
3846   PreparePreviewTileBitmap_BD(src_bitmap, scale_down_factor);
3847
3848   // force using special preview bitmap to replace original preview bitmap
3849   getSizedGraphicSource(graphic, 0, preview.tile_size, &src_bitmap, &src_x, &src_y);
3850   SetPreviewTileBitmapReference_BD(src_bitmap);
3851 }
3852
3853 void DrawPreviewLevelInitial(void)
3854 {
3855   PreparePreviewTileBitmap();   // only needed for native BD style levels
3856
3857   DrawPreviewLevelExt(TRUE);
3858   DrawPreviewPlayers();
3859 }
3860
3861 void DrawPreviewLevelAnimation(void)
3862 {
3863   DrawPreviewLevelExt(FALSE);
3864 }
3865
3866 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3867                               int border_size, int font_nr)
3868 {
3869   int graphic = el2img(EL_PLAYER_1 + player_nr);
3870   int font_height = getFontHeight(font_nr);
3871   int player_height = MAX(tile_size, font_height);
3872   int xoffset_text = tile_size + border_size;
3873   int yoffset_text    = (player_height - font_height) / 2;
3874   int yoffset_graphic = (player_height - tile_size) / 2;
3875   char *player_name = getNetworkPlayerName(player_nr + 1);
3876
3877   DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3878                               tile_size);
3879   DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3880 }
3881
3882 static void DrawNetworkPlayersExt(boolean force)
3883 {
3884   if (game_status != GAME_MODE_MAIN)
3885     return;
3886
3887   if (!network.connected && !force)
3888     return;
3889
3890   // do not draw network players if level preview redefined, but players aren't
3891   if (preview.redefined && !menu.main.network_players.redefined)
3892     return;
3893
3894   int num_players = 0;
3895   int i;
3896
3897   for (i = 0; i < MAX_PLAYERS; i++)
3898     if (stored_player[i].connected_network)
3899       num_players++;
3900
3901   struct TextPosInfo *pos = &menu.main.network_players;
3902   int tile_size = pos->tile_size;
3903   int border_size = pos->border_size;
3904   int xoffset_text = tile_size + border_size;
3905   int font_nr = pos->font;
3906   int font_width = getFontWidth(font_nr);
3907   int font_height = getFontHeight(font_nr);
3908   int player_height = MAX(tile_size, font_height);
3909   int player_yoffset = player_height + border_size;
3910   int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3911   int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3912   int all_players_height = num_players * player_yoffset - border_size;
3913   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3914   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3915   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3916
3917   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3918                              max_players_width, max_players_height);
3919
3920   // first draw local network player ...
3921   for (i = 0; i < MAX_PLAYERS; i++)
3922   {
3923     if (stored_player[i].connected_network &&
3924         stored_player[i].connected_locally)
3925     {
3926       char *player_name = getNetworkPlayerName(i + 1);
3927       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3928       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3929
3930       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3931
3932       ypos += player_yoffset;
3933     }
3934   }
3935
3936   // ... then draw all other network players
3937   for (i = 0; i < MAX_PLAYERS; i++)
3938   {
3939     if (stored_player[i].connected_network &&
3940         !stored_player[i].connected_locally)
3941     {
3942       char *player_name = getNetworkPlayerName(i + 1);
3943       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3944       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3945
3946       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3947
3948       ypos += player_yoffset;
3949     }
3950   }
3951 }
3952
3953 void DrawNetworkPlayers(void)
3954 {
3955   DrawNetworkPlayersExt(FALSE);
3956 }
3957
3958 void ClearNetworkPlayers(void)
3959 {
3960   DrawNetworkPlayersExt(TRUE);
3961 }
3962
3963 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3964                                     int graphic, int lx, int ly,
3965                                     int mask_mode)
3966 {
3967   int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3968
3969   if (mask_mode == USE_MASKING)
3970     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3971   else
3972     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3973 }
3974
3975 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3976                                   int graphic, int sync_frame, int mask_mode)
3977 {
3978   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3979
3980   if (mask_mode == USE_MASKING)
3981     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3982   else
3983     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3984 }
3985
3986 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3987                                   int graphic, int sync_frame, int tilesize,
3988                                   int mask_mode)
3989 {
3990   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3991
3992   if (mask_mode == USE_MASKING)
3993     DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3994   else
3995     DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3996 }
3997
3998 static void DrawGraphicAnimation(int x, int y, int graphic)
3999 {
4000   int lx = LEVELX(x), ly = LEVELY(y);
4001   int mask_mode = NO_MASKING;
4002
4003   if (!IN_SCR_FIELD(x, y))
4004     return;
4005
4006   if (game.use_masked_elements)
4007   {
4008     if (Tile[lx][ly] != EL_EMPTY)
4009     {
4010       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4011
4012       mask_mode = USE_MASKING;
4013     }
4014   }
4015
4016   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
4017                           graphic, lx, ly, mask_mode);
4018
4019   MarkTileDirty(x, y);
4020 }
4021
4022 void DrawFixedGraphicAnimation(int x, int y, int graphic)
4023 {
4024   int lx = LEVELX(x), ly = LEVELY(y);
4025   int mask_mode = NO_MASKING;
4026
4027   if (!IN_SCR_FIELD(x, y))
4028     return;
4029
4030   if (game.use_masked_elements)
4031   {
4032     if (Tile[lx][ly] != EL_EMPTY)
4033     {
4034       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4035
4036       mask_mode = USE_MASKING;
4037     }
4038   }
4039
4040   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
4041                           graphic, lx, ly, mask_mode);
4042
4043   MarkTileDirty(x, y);
4044 }
4045
4046 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4047 {
4048   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4049 }
4050
4051 void DrawLevelElementAnimation(int x, int y, int element)
4052 {
4053   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4054
4055   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4056 }
4057
4058 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4059 {
4060   int sx = SCREENX(x), sy = SCREENY(y);
4061
4062   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4063     return;
4064
4065   if (Tile[x][y] == EL_EMPTY)
4066     graphic = el2img(GfxElementEmpty[x][y]);
4067
4068   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4069     return;
4070
4071   if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4072     return;
4073
4074   DrawGraphicAnimation(sx, sy, graphic);
4075
4076 #if 1
4077   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4078     DrawLevelFieldCrumbled(x, y);
4079 #else
4080   if (GFX_CRUMBLED(Tile[x][y]))
4081     DrawLevelFieldCrumbled(x, y);
4082 #endif
4083 }
4084
4085 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4086 {
4087   int sx = SCREENX(x), sy = SCREENY(y);
4088   int graphic;
4089
4090   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4091     return;
4092
4093   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4094
4095   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4096     return;
4097
4098   DrawGraphicAnimation(sx, sy, graphic);
4099
4100   if (GFX_CRUMBLED(element))
4101     DrawLevelFieldCrumbled(x, y);
4102 }
4103
4104 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4105 {
4106   if (player->use_murphy)
4107   {
4108     // this works only because currently only one player can be "murphy" ...
4109     static int last_horizontal_dir = MV_LEFT;
4110     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4111
4112     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4113       last_horizontal_dir = move_dir;
4114
4115     if (graphic == IMG_SP_MURPHY)       // undefined => use special graphic
4116     {
4117       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4118
4119       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4120     }
4121
4122     return graphic;
4123   }
4124   else
4125     return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4126 }
4127
4128 static boolean equalGraphics(int graphic1, int graphic2)
4129 {
4130   struct GraphicInfo *g1 = &graphic_info[graphic1];
4131   struct GraphicInfo *g2 = &graphic_info[graphic2];
4132
4133   return (g1->bitmap      == g2->bitmap &&
4134           g1->src_x       == g2->src_x &&
4135           g1->src_y       == g2->src_y &&
4136           g1->anim_frames == g2->anim_frames &&
4137           g1->anim_delay  == g2->anim_delay &&
4138           g1->anim_mode   == g2->anim_mode);
4139 }
4140
4141 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4142
4143 enum
4144 {
4145   DRAW_PLAYER_STAGE_INIT = 0,
4146   DRAW_PLAYER_STAGE_LAST_FIELD,
4147   DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4148 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4149   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4150   DRAW_PLAYER_STAGE_PLAYER,
4151 #else
4152   DRAW_PLAYER_STAGE_PLAYER,
4153   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4154 #endif
4155   DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4156   DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4157
4158   NUM_DRAW_PLAYER_STAGES
4159 };
4160
4161 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4162 {
4163   static int static_last_player_graphic[MAX_PLAYERS];
4164   static int static_last_player_frame[MAX_PLAYERS];
4165   static boolean static_player_is_opaque[MAX_PLAYERS];
4166   static boolean draw_player[MAX_PLAYERS];
4167   int pnr = player->index_nr;
4168
4169   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4170   {
4171     static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4172     static_last_player_frame[pnr] = player->Frame;
4173     static_player_is_opaque[pnr] = FALSE;
4174
4175     draw_player[pnr] = TRUE;
4176   }
4177
4178   if (!draw_player[pnr])
4179     return;
4180
4181 #if DEBUG
4182   if (!IN_LEV_FIELD(player->jx, player->jy))
4183   {
4184     Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4185     Debug("draw:DrawPlayerExt", "This should never happen!");
4186
4187     draw_player[pnr] = FALSE;
4188
4189     return;
4190   }
4191 #endif
4192
4193   int last_player_graphic  = static_last_player_graphic[pnr];
4194   int last_player_frame    = static_last_player_frame[pnr];
4195   boolean player_is_opaque = static_player_is_opaque[pnr];
4196
4197   int jx = player->jx;
4198   int jy = player->jy;
4199   int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4200   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4201   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
4202   int last_jx = (player->is_moving ? jx - dx : jx);
4203   int last_jy = (player->is_moving ? jy - dy : jy);
4204   int next_jx = jx + dx;
4205   int next_jy = jy + dy;
4206   boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4207   int sx = SCREENX(jx);
4208   int sy = SCREENY(jy);
4209   int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4210   int syy = (move_dir == MV_UP   || move_dir == MV_DOWN  ? player->GfxPos : 0);
4211   int element = Tile[jx][jy];
4212   int last_element = Tile[last_jx][last_jy];
4213   int action = (player->is_pushing    ? ACTION_PUSHING         :
4214                 player->is_digging    ? ACTION_DIGGING         :
4215                 player->is_collecting ? ACTION_COLLECTING      :
4216                 player->is_moving     ? ACTION_MOVING          :
4217                 player->is_snapping   ? ACTION_SNAPPING        :
4218                 player->is_dropping   ? ACTION_DROPPING        :
4219                 player->is_waiting    ? player->action_waiting :
4220                 ACTION_DEFAULT);
4221
4222   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4223   {
4224     // ------------------------------------------------------------------------
4225     // initialize drawing the player
4226     // ------------------------------------------------------------------------
4227
4228     draw_player[pnr] = FALSE;
4229
4230     // GfxElement[][] is set to the element the player is digging or collecting;
4231     // remove also for off-screen player if the player is not moving anymore
4232     if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4233       GfxElement[jx][jy] = EL_UNDEFINED;
4234
4235     if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4236       return;
4237
4238     if (element == EL_EXPLOSION)
4239       return;
4240
4241     InitPlayerGfxAnimation(player, action, move_dir);
4242
4243     draw_player[pnr] = TRUE;
4244   }
4245   else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4246   {
4247     // ------------------------------------------------------------------------
4248     // draw things in the field the player is leaving, if needed
4249     // ------------------------------------------------------------------------
4250
4251     if (!IN_SCR_FIELD(sx, sy))
4252       draw_player[pnr] = FALSE;
4253
4254     if (!player->is_moving)
4255       return;
4256
4257     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4258     {
4259       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4260
4261       if (last_element == EL_DYNAMITE_ACTIVE ||
4262           last_element == EL_EM_DYNAMITE_ACTIVE ||
4263           last_element == EL_SP_DISK_RED_ACTIVE)
4264         DrawDynamite(last_jx, last_jy);
4265       else
4266         DrawLevelFieldThruMask(last_jx, last_jy);
4267     }
4268     else if (last_element == EL_DYNAMITE_ACTIVE ||
4269              last_element == EL_EM_DYNAMITE_ACTIVE ||
4270              last_element == EL_SP_DISK_RED_ACTIVE)
4271       DrawDynamite(last_jx, last_jy);
4272     else
4273       DrawLevelField(last_jx, last_jy);
4274   }
4275   else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4276   {
4277     // ------------------------------------------------------------------------
4278     // draw things behind the player, if needed
4279     // ------------------------------------------------------------------------
4280
4281     if (Back[jx][jy])
4282     {
4283       DrawLevelElement(jx, jy, Back[jx][jy]);
4284
4285       return;
4286     }
4287
4288     if (IS_ACTIVE_BOMB(element))
4289     {
4290       DrawLevelElement(jx, jy, EL_EMPTY);
4291
4292       return;
4293     }
4294
4295     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4296     {
4297       int old_element = GfxElement[jx][jy];
4298       int old_graphic = el_act_dir2img(old_element, action, move_dir);
4299       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4300
4301       if (GFX_CRUMBLED(old_element))
4302         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4303       else
4304         DrawScreenGraphic(sx, sy, old_graphic, frame);
4305
4306       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4307         static_player_is_opaque[pnr] = TRUE;
4308     }
4309     else
4310     {
4311       GfxElement[jx][jy] = EL_UNDEFINED;
4312
4313       // make sure that pushed elements are drawn with correct frame rate
4314       int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4315
4316       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4317         GfxFrame[jx][jy] = player->StepFrame;
4318
4319       DrawLevelField(jx, jy);
4320     }
4321   }
4322   else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4323   {
4324     // ------------------------------------------------------------------------
4325     // draw things the player is pushing, if needed
4326     // ------------------------------------------------------------------------
4327
4328     if (!player->is_pushing || !player->is_moving)
4329       return;
4330
4331     if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4332       return;
4333
4334     int gfx_frame = GfxFrame[jx][jy];
4335
4336     if (!IS_MOVING(jx, jy))             // push movement already finished
4337     {
4338       element = Tile[next_jx][next_jy];
4339       gfx_frame = GfxFrame[next_jx][next_jy];
4340     }
4341
4342     int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4343     int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4344     int frame = getGraphicAnimationFrame(graphic, sync_frame);
4345
4346     // draw background element under pushed element (like the Sokoban field)
4347     if (game.use_masked_pushing && IS_MOVING(jx, jy))
4348     {
4349       // this allows transparent pushing animation over non-black background
4350
4351       if (Back[jx][jy])
4352         DrawLevelElement(jx, jy, Back[jx][jy]);
4353       else
4354         DrawLevelElement(jx, jy, EL_EMPTY);
4355     }
4356
4357     if (Back[next_jx][next_jy])
4358       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4359     else
4360       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4361
4362     int px = SCREENX(jx), py = SCREENY(jy);
4363     int pxx = (TILEX - ABS(sxx)) * dx;
4364     int pyy = (TILEY - ABS(syy)) * dy;
4365
4366 #if 1
4367     // do not draw (EM style) pushing animation when pushing is finished
4368     // (two-tile animations usually do not contain start and end frame)
4369     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4370       DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4371     else
4372       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4373 #else
4374     // masked drawing is needed for EMC style (double) movement graphics
4375     // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4376     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4377 #endif
4378   }
4379   else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4380   {
4381     // ------------------------------------------------------------------------
4382     // draw player himself
4383     // ------------------------------------------------------------------------
4384
4385     int graphic = getPlayerGraphic(player, move_dir);
4386
4387     // in the case of changed player action or direction, prevent the current
4388     // animation frame from being restarted for identical animations
4389     if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4390       player->Frame = last_player_frame;
4391
4392     int frame = getGraphicAnimationFrame(graphic, player->Frame);
4393
4394     if (player_is_opaque)
4395       DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4396     else
4397       DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4398
4399     if (SHIELD_ON(player))
4400     {
4401       graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4402                  IMG_SHIELD_NORMAL_ACTIVE);
4403       frame = getGraphicAnimationFrame(graphic, -1);
4404
4405       DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4406     }
4407   }
4408   else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4409   {
4410     // ------------------------------------------------------------------------
4411     // draw things in front of player (active dynamite or dynabombs)
4412     // ------------------------------------------------------------------------
4413
4414     if (IS_ACTIVE_BOMB(element))
4415     {
4416       int graphic = el2img(element);
4417       int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4418
4419       if (game.emulation == EMU_SUPAPLEX)
4420         DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4421       else
4422         DrawGraphicThruMask(sx, sy, graphic, frame);
4423     }
4424
4425     if (player_is_moving && last_element == EL_EXPLOSION)
4426     {
4427       int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4428                      GfxElement[last_jx][last_jy] :  EL_EMPTY);
4429       int graphic = el_act2img(element, ACTION_EXPLODING);
4430       int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4431       int phase = ExplodePhase[last_jx][last_jy] - 1;
4432       int frame = getGraphicAnimationFrame(graphic, phase - delay);
4433
4434       if (phase >= delay)
4435         DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4436     }
4437   }
4438   else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4439   {
4440     // ------------------------------------------------------------------------
4441     // draw elements the player is just walking/passing through/under
4442     // ------------------------------------------------------------------------
4443
4444     if (player_is_moving)
4445     {
4446       // handle the field the player is leaving ...
4447       if (IS_ACCESSIBLE_INSIDE(last_element))
4448         DrawLevelField(last_jx, last_jy);
4449       else if (IS_ACCESSIBLE_UNDER(last_element))
4450         DrawLevelFieldThruMask(last_jx, last_jy);
4451     }
4452
4453     // do not redraw accessible elements if the player is just pushing them
4454     if (!player_is_moving || !player->is_pushing)
4455     {
4456       // ... and the field the player is entering
4457       if (IS_ACCESSIBLE_INSIDE(element))
4458         DrawLevelField(jx, jy);
4459       else if (IS_ACCESSIBLE_UNDER(element))
4460         DrawLevelFieldThruMask(jx, jy);
4461     }
4462
4463     MarkTileDirty(sx, sy);
4464   }
4465 }
4466
4467 void DrawPlayer(struct PlayerInfo *player)
4468 {
4469   int i;
4470
4471   for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4472     DrawPlayerExt(player, i);
4473 }
4474
4475 void DrawAllPlayers(void)
4476 {
4477   int i, j;
4478
4479   for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4480     for (j = 0; j < MAX_PLAYERS; j++)
4481       if (stored_player[j].active)
4482         DrawPlayerExt(&stored_player[j], i);
4483 }
4484
4485 void DrawPlayerField(int x, int y)
4486 {
4487   if (!IS_PLAYER(x, y))
4488     return;
4489
4490   DrawPlayer(PLAYERINFO(x, y));
4491 }
4492
4493 // ----------------------------------------------------------------------------
4494
4495 void WaitForEventToContinue(void)
4496 {
4497   boolean first_wait = TRUE;
4498   boolean still_wait = TRUE;
4499
4500   if (program.headless)
4501     return;
4502
4503   // simulate releasing mouse button over last gadget, if still pressed
4504   if (button_status)
4505     HandleGadgets(-1, -1, 0);
4506
4507   button_status = MB_RELEASED;
4508
4509   ClearEventQueue();
4510   ClearPlayerAction();
4511
4512   while (still_wait)
4513   {
4514     Event event;
4515
4516     if (NextValidEvent(&event))
4517     {
4518       switch (event.type)
4519       {
4520         case EVENT_BUTTONPRESS:
4521         case EVENT_FINGERPRESS:
4522           first_wait = FALSE;
4523           break;
4524
4525         case EVENT_BUTTONRELEASE:
4526         case EVENT_FINGERRELEASE:
4527           still_wait = first_wait;
4528           break;
4529
4530         case EVENT_KEYPRESS:
4531         case SDL_CONTROLLERBUTTONDOWN:
4532         case SDL_JOYBUTTONDOWN:
4533           still_wait = FALSE;
4534           break;
4535
4536         default:
4537           HandleOtherEvents(&event);
4538           break;
4539       }
4540     }
4541     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4542     {
4543       still_wait = FALSE;
4544     }
4545
4546     if (!PendingEvent())
4547       BackToFront();
4548   }
4549 }
4550
4551 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4552 {
4553   boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4554   int draw_buffer_last = GetDrawtoField();
4555   int width  = request.width;
4556   int height = request.height;
4557   int sx, sy;
4558   int result;
4559
4560   setRequestPosition(&sx, &sy, FALSE);
4561
4562   button_status = MB_RELEASED;
4563
4564   request_gadget_id = -1;
4565   result = -1;
4566
4567   while (result < 0)
4568   {
4569     if (game_ended)
4570     {
4571       SetDrawtoField(draw_buffer_game);
4572
4573       HandleGameActions();
4574
4575       SetDrawtoField(DRAW_TO_BACKBUFFER);
4576     }
4577
4578     if (PendingEvent())
4579     {
4580       Event event;
4581
4582       while (NextValidEvent(&event))
4583       {
4584         switch (event.type)
4585         {
4586           case EVENT_BUTTONPRESS:
4587           case EVENT_BUTTONRELEASE:
4588           case EVENT_MOTIONNOTIFY:
4589           {
4590             DrawBuffer *drawto_last = drawto;
4591             int mx, my;
4592
4593             if (event.type == EVENT_MOTIONNOTIFY)
4594             {
4595               if (!button_status)
4596                 continue;
4597
4598               motion_status = TRUE;
4599               mx = ((MotionEvent *) &event)->x;
4600               my = ((MotionEvent *) &event)->y;
4601             }
4602             else
4603             {
4604               motion_status = FALSE;
4605               mx = ((ButtonEvent *) &event)->x;
4606               my = ((ButtonEvent *) &event)->y;
4607               if (event.type == EVENT_BUTTONPRESS)
4608                 button_status = ((ButtonEvent *) &event)->button;
4609               else
4610                 button_status = MB_RELEASED;
4611             }
4612
4613             if (global.use_envelope_request)
4614             {
4615               // draw changed button states to temporary bitmap
4616               drawto = bitmap_db_store_1;
4617             }
4618
4619             // this sets 'request_gadget_id'
4620             HandleGadgets(mx, my, button_status);
4621
4622             if (global.use_envelope_request)
4623             {
4624               // restore pointer to drawing buffer
4625               drawto = drawto_last;
4626
4627               // prepare complete envelope request from temporary bitmap
4628               PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4629                                              width, height);
4630             }
4631
4632             switch (request_gadget_id)
4633             {
4634               case TOOL_CTRL_ID_YES:
4635               case TOOL_CTRL_ID_TOUCH_YES:
4636                 result = TRUE;
4637                 break;
4638               case TOOL_CTRL_ID_NO:
4639               case TOOL_CTRL_ID_TOUCH_NO:
4640                 result = FALSE;
4641                 break;
4642               case TOOL_CTRL_ID_CONFIRM:
4643               case TOOL_CTRL_ID_TOUCH_CONFIRM:
4644                 result = TRUE | FALSE;
4645                 break;
4646
4647               case TOOL_CTRL_ID_PLAYER_1:
4648                 result = 1;
4649                 break;
4650               case TOOL_CTRL_ID_PLAYER_2:
4651                 result = 2;
4652                 break;
4653               case TOOL_CTRL_ID_PLAYER_3:
4654                 result = 3;
4655                 break;
4656               case TOOL_CTRL_ID_PLAYER_4:
4657                 result = 4;
4658                 break;
4659
4660               default:
4661                 break;
4662             }
4663
4664             // only needed to handle clickable pointer animations here
4665             HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4666
4667             break;
4668           }
4669
4670           case SDL_WINDOWEVENT:
4671             HandleWindowEvent((WindowEvent *) &event);
4672             break;
4673
4674           case SDL_APP_WILLENTERBACKGROUND:
4675           case SDL_APP_DIDENTERBACKGROUND:
4676           case SDL_APP_WILLENTERFOREGROUND:
4677           case SDL_APP_DIDENTERFOREGROUND:
4678             HandlePauseResumeEvent((PauseResumeEvent *) &event);
4679             break;
4680
4681           case EVENT_KEYPRESS:
4682           {
4683             Key key = GetEventKey((KeyEvent *)&event);
4684
4685             switch (key)
4686             {
4687               case KSYM_space:
4688                 if (req_state & REQ_CONFIRM)
4689                   result = 1;
4690                 break;
4691
4692               case KSYM_Return:
4693               case KSYM_y:
4694               case KSYM_Y:
4695               case KSYM_Select:
4696               case KSYM_Menu:
4697 #if defined(KSYM_Rewind)
4698               case KSYM_Rewind:         // for Amazon Fire TV remote
4699 #endif
4700                 result = 1;
4701                 break;
4702
4703               case KSYM_Escape:
4704               case KSYM_n:
4705               case KSYM_N:
4706               case KSYM_Back:
4707 #if defined(KSYM_FastForward)
4708               case KSYM_FastForward:    // for Amazon Fire TV remote
4709 #endif
4710                 result = 0;
4711                 break;
4712
4713               default:
4714                 HandleKeysDebug(key, KEY_PRESSED);
4715                 break;
4716             }
4717
4718             if (req_state & REQ_PLAYER)
4719             {
4720               int old_player_nr = setup.network_player_nr;
4721
4722               if (result != -1)
4723                 result = old_player_nr + 1;
4724
4725               switch (key)
4726               {
4727                 case KSYM_space:
4728                   result = old_player_nr + 1;
4729                   break;
4730
4731                 case KSYM_Up:
4732                 case KSYM_1:
4733                   result = 1;
4734                   break;
4735
4736                 case KSYM_Right:
4737                 case KSYM_2:
4738                   result = 2;
4739                   break;
4740
4741                 case KSYM_Down:
4742                 case KSYM_3:
4743                   result = 3;
4744                   break;
4745
4746                 case KSYM_Left:
4747                 case KSYM_4:
4748                   result = 4;
4749                   break;
4750
4751                 default:
4752                   break;
4753               }
4754             }
4755
4756             break;
4757           }
4758
4759           case EVENT_FINGERRELEASE:
4760           case EVENT_KEYRELEASE:
4761             ClearPlayerAction();
4762             break;
4763
4764           case SDL_CONTROLLERBUTTONDOWN:
4765             switch (event.cbutton.button)
4766             {
4767               case SDL_CONTROLLER_BUTTON_A:
4768               case SDL_CONTROLLER_BUTTON_X:
4769               case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4770               case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4771                 result = 1;
4772                 break;
4773
4774               case SDL_CONTROLLER_BUTTON_B:
4775               case SDL_CONTROLLER_BUTTON_Y:
4776               case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4777               case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4778               case SDL_CONTROLLER_BUTTON_BACK:
4779                 result = 0;
4780                 break;
4781             }
4782
4783             if (req_state & REQ_PLAYER)
4784             {
4785               int old_player_nr = setup.network_player_nr;
4786
4787               if (result != -1)
4788                 result = old_player_nr + 1;
4789
4790               switch (event.cbutton.button)
4791               {
4792                 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4793                 case SDL_CONTROLLER_BUTTON_Y:
4794                   result = 1;
4795                   break;
4796
4797                 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4798                 case SDL_CONTROLLER_BUTTON_B:
4799                   result = 2;
4800                   break;
4801
4802                 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4803                 case SDL_CONTROLLER_BUTTON_A:
4804                   result = 3;
4805                   break;
4806
4807                 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4808                 case SDL_CONTROLLER_BUTTON_X:
4809                   result = 4;
4810                   break;
4811
4812                 default:
4813                   break;
4814               }
4815             }
4816
4817             break;
4818
4819           case SDL_CONTROLLERBUTTONUP:
4820             HandleJoystickEvent(&event);
4821             ClearPlayerAction();
4822             break;
4823
4824           default:
4825             HandleOtherEvents(&event);
4826             break;
4827         }
4828       }
4829     }
4830     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4831     {
4832       int joy = AnyJoystick();
4833
4834       if (joy & JOY_BUTTON_1)
4835         result = 1;
4836       else if (joy & JOY_BUTTON_2)
4837         result = 0;
4838     }
4839     else if (AnyJoystick())
4840     {
4841       int joy = AnyJoystick();
4842
4843       if (req_state & REQ_PLAYER)
4844       {
4845         if (joy & JOY_UP)
4846           result = 1;
4847         else if (joy & JOY_RIGHT)
4848           result = 2;
4849         else if (joy & JOY_DOWN)
4850           result = 3;
4851         else if (joy & JOY_LEFT)
4852           result = 4;
4853       }
4854     }
4855
4856     BackToFront();
4857   }
4858
4859   SetDrawtoField(draw_buffer_last);
4860
4861   return result;
4862 }
4863
4864 static void DoRequestBefore(void)
4865 {
4866   boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4867
4868   // when showing request dialog after game ended, deactivate game panel
4869   if (game_ended)
4870     game.panel.active = FALSE;
4871
4872   if (game_status == GAME_MODE_PLAYING)
4873     BlitScreenToBitmap(backbuffer);
4874
4875   // disable deactivated drawing when quick-loading level tape recording
4876   if (tape.playing && tape.deactivate_display)
4877     TapeDeactivateDisplayOff(TRUE);
4878
4879   SetMouseCursor(CURSOR_DEFAULT);
4880
4881   // pause network game while waiting for request to answer
4882   if (network.enabled &&
4883       game_status == GAME_MODE_PLAYING &&
4884       !game.all_players_gone)
4885     SendToServer_PausePlaying();
4886
4887   // simulate releasing mouse button over last gadget, if still pressed
4888   if (button_status)
4889     HandleGadgets(-1, -1, 0);
4890
4891   UnmapAllGadgets();
4892 }
4893
4894 static void DoRequestAfter(void)
4895 {
4896   RemapAllGadgets();
4897
4898   if (game_status == GAME_MODE_PLAYING)
4899   {
4900     SetPanelBackground();
4901     SetDrawBackgroundMask(REDRAW_DOOR_1);
4902   }
4903   else
4904   {
4905     SetDrawBackgroundMask(REDRAW_FIELD);
4906   }
4907
4908   // continue network game after request
4909   if (network.enabled &&
4910       game_status == GAME_MODE_PLAYING &&
4911       !game.all_players_gone)
4912     SendToServer_ContinuePlaying();
4913
4914   // restore deactivated drawing when quick-loading level tape recording
4915   if (tape.playing && tape.deactivate_display)
4916     TapeDeactivateDisplayOn();
4917 }
4918
4919 static void setRequestDoorTextProperties(char *text,
4920                                          int text_spacing,
4921                                          int line_spacing,
4922                                          int *set_font_nr,
4923                                          int *set_max_lines,
4924                                          int *set_max_line_length)
4925 {
4926   struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4927   struct TextPosInfo *pos = &request.button.confirm;
4928   int button_ypos = pos->y;
4929   int font_nr = FONT_TEXT_2;
4930   int font_width = getFontWidth(font_nr);
4931   int font_height = getFontHeight(font_nr);
4932   int line_height = font_height + line_spacing;
4933   int max_text_width  = vp_door_1->width;
4934   int max_text_height = button_ypos - 2 * text_spacing;
4935   int max_line_length = max_text_width  / font_width;
4936   int max_lines       = max_text_height / line_height;
4937
4938   if (maxWordLengthInRequestString(text) > max_line_length)
4939   {
4940     font_nr = FONT_TEXT_1;
4941     font_width = getFontWidth(font_nr);
4942     max_line_length = max_text_width  / font_width;
4943   }
4944
4945   *set_font_nr = font_nr;
4946   *set_max_lines = max_lines;
4947   *set_max_line_length = max_line_length;
4948 }
4949
4950 static void DrawRequestDoorText(char *text)
4951 {
4952   char *text_ptr = text;
4953   int text_spacing = 8;
4954   int line_spacing = 2;
4955   int max_request_lines;
4956   int max_request_line_len;
4957   int font_nr;
4958   int ty;
4959
4960   // force DOOR font inside door area
4961   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4962
4963   setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4964                                &max_request_lines, &max_request_line_len);
4965
4966   for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4967   {
4968     char text_line[max_request_line_len + 1];
4969     int tx, tl, tc = 0;
4970
4971     if (!*text_ptr)
4972       break;
4973
4974     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4975     {
4976       tc = *(text_ptr + tx);
4977       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4978         break;
4979     }
4980
4981     if ((tc == '?' || tc == '!') && tl == 0)
4982       tl = 1;
4983
4984     if (!tl)
4985     { 
4986       text_ptr++; 
4987       ty--; 
4988       continue; 
4989     }
4990
4991     strncpy(text_line, text_ptr, tl);
4992     text_line[tl] = 0;
4993
4994     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4995              DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4996              text_line, font_nr);
4997
4998     text_ptr += tl + (tc == ' ' ? 1 : 0);
4999   }
5000
5001   ResetFontStatus();
5002 }
5003
5004 static int RequestDoor(char *text, unsigned int req_state)
5005 {
5006   unsigned int old_door_state = GetDoorState();
5007   int draw_buffer_last = GetDrawtoField();
5008   int result;
5009
5010   if (old_door_state & DOOR_OPEN_1)
5011   {
5012     CloseDoor(DOOR_CLOSE_1);
5013
5014     // save old door content
5015     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5016                0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
5017   }
5018
5019   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5020   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5021
5022   // clear door drawing field
5023   DrawBackground(DX, DY, DXSIZE, DYSIZE);
5024
5025   // write text for request
5026   DrawRequestDoorText(text);
5027
5028   MapToolButtons(req_state);
5029
5030   // copy request gadgets to door backbuffer
5031   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
5032
5033   OpenDoor(DOOR_OPEN_1);
5034
5035   // ---------- handle request buttons ----------
5036   result = RequestHandleEvents(req_state, draw_buffer_last);
5037
5038   UnmapToolButtons();
5039
5040   if (!(req_state & REQ_STAY_OPEN))
5041   {
5042     CloseDoor(DOOR_CLOSE_1);
5043
5044     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
5045         (req_state & REQ_REOPEN))
5046       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5047   }
5048
5049   return result;
5050 }
5051
5052 static int RequestEnvelope(char *text, unsigned int req_state)
5053 {
5054   int draw_buffer_last = GetDrawtoField();
5055   int result;
5056
5057   DrawEnvelopeRequest(text, req_state);
5058   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5059
5060   // ---------- handle request buttons ----------
5061   result = RequestHandleEvents(req_state, draw_buffer_last);
5062
5063   UnmapToolButtons();
5064
5065   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5066
5067   return result;
5068 }
5069
5070 int Request(char *text, unsigned int req_state)
5071 {
5072   boolean overlay_enabled = GetOverlayEnabled();
5073   int result;
5074
5075   game.request_active = TRUE;
5076
5077   SetOverlayEnabled(FALSE);
5078
5079   DoRequestBefore();
5080
5081   if (global.use_envelope_request)
5082     result = RequestEnvelope(text, req_state);
5083   else
5084     result = RequestDoor(text, req_state);
5085
5086   DoRequestAfter();
5087
5088   SetOverlayEnabled(overlay_enabled);
5089
5090   game.request_active = FALSE;
5091
5092   return result;
5093 }
5094
5095 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5096 {
5097   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5098   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5099   int compare_result;
5100
5101   if (dpo1->sort_priority != dpo2->sort_priority)
5102     compare_result = dpo1->sort_priority - dpo2->sort_priority;
5103   else
5104     compare_result = dpo1->nr - dpo2->nr;
5105
5106   return compare_result;
5107 }
5108
5109 void InitGraphicCompatibilityInfo_Doors(void)
5110 {
5111   struct
5112   {
5113     int door_token;
5114     int part_1, part_8;
5115     struct DoorInfo *door;
5116   }
5117   doors[] =
5118   {
5119     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
5120     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
5121
5122     { -1,       -1,                     -1,                     NULL    }
5123   };
5124   struct Rect door_rect_list[] =
5125   {
5126     { DX, DY, DXSIZE, DYSIZE },
5127     { VX, VY, VXSIZE, VYSIZE }
5128   };
5129   int i, j;
5130
5131   for (i = 0; doors[i].door_token != -1; i++)
5132   {
5133     int door_token = doors[i].door_token;
5134     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5135     int part_1 = doors[i].part_1;
5136     int part_8 = doors[i].part_8;
5137     int part_2 = part_1 + 1;
5138     int part_3 = part_1 + 2;
5139     struct DoorInfo *door = doors[i].door;
5140     struct Rect *door_rect = &door_rect_list[door_index];
5141     boolean door_gfx_redefined = FALSE;
5142
5143     // check if any door part graphic definitions have been redefined
5144
5145     for (j = 0; door_part_controls[j].door_token != -1; j++)
5146     {
5147       struct DoorPartControlInfo *dpc = &door_part_controls[j];
5148       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5149
5150       if (dpc->door_token == door_token && fi->redefined)
5151         door_gfx_redefined = TRUE;
5152     }
5153
5154     // check for old-style door graphic/animation modifications
5155
5156     if (!door_gfx_redefined)
5157     {
5158       if (door->anim_mode & ANIM_STATIC_PANEL)
5159       {
5160         door->panel.step_xoffset = 0;
5161         door->panel.step_yoffset = 0;
5162       }
5163
5164       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5165       {
5166         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5167         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5168         int num_door_steps, num_panel_steps;
5169
5170         // remove door part graphics other than the two default wings
5171
5172         for (j = 0; door_part_controls[j].door_token != -1; j++)
5173         {
5174           struct DoorPartControlInfo *dpc = &door_part_controls[j];
5175           struct GraphicInfo *g = &graphic_info[dpc->graphic];
5176
5177           if (dpc->graphic >= part_3 &&
5178               dpc->graphic <= part_8)
5179             g->bitmap = NULL;
5180         }
5181
5182         // set graphics and screen positions of the default wings
5183
5184         g_part_1->width  = door_rect->width;
5185         g_part_1->height = door_rect->height;
5186         g_part_2->width  = door_rect->width;
5187         g_part_2->height = door_rect->height;
5188         g_part_2->src_x = door_rect->width;
5189         g_part_2->src_y = g_part_1->src_y;
5190
5191         door->part_2.x = door->part_1.x;
5192         door->part_2.y = door->part_1.y;
5193
5194         if (door->width != -1)
5195         {
5196           g_part_1->width = door->width;
5197           g_part_2->width = door->width;
5198
5199           // special treatment for graphics and screen position of right wing
5200           g_part_2->src_x += door_rect->width - door->width;
5201           door->part_2.x  += door_rect->width - door->width;
5202         }
5203
5204         if (door->height != -1)
5205         {
5206           g_part_1->height = door->height;
5207           g_part_2->height = door->height;
5208
5209           // special treatment for graphics and screen position of bottom wing
5210           g_part_2->src_y += door_rect->height - door->height;
5211           door->part_2.y  += door_rect->height - door->height;
5212         }
5213
5214         // set animation delays for the default wings and panels
5215
5216         door->part_1.step_delay = door->step_delay;
5217         door->part_2.step_delay = door->step_delay;
5218         door->panel.step_delay  = door->step_delay;
5219
5220         // set animation draw order for the default wings
5221
5222         door->part_1.sort_priority = 2; // draw left wing over ...
5223         door->part_2.sort_priority = 1; //          ... right wing
5224
5225         // set animation draw offset for the default wings
5226
5227         if (door->anim_mode & ANIM_HORIZONTAL)
5228         {
5229           door->part_1.step_xoffset = door->step_offset;
5230           door->part_1.step_yoffset = 0;
5231           door->part_2.step_xoffset = door->step_offset * -1;
5232           door->part_2.step_yoffset = 0;
5233
5234           num_door_steps = g_part_1->width / door->step_offset;
5235         }
5236         else    // ANIM_VERTICAL
5237         {
5238           door->part_1.step_xoffset = 0;
5239           door->part_1.step_yoffset = door->step_offset;
5240           door->part_2.step_xoffset = 0;
5241           door->part_2.step_yoffset = door->step_offset * -1;
5242
5243           num_door_steps = g_part_1->height / door->step_offset;
5244         }
5245
5246         // set animation draw offset for the default panels
5247
5248         if (door->step_offset > 1)
5249         {
5250           num_panel_steps = 2 * door_rect->height / door->step_offset;
5251           door->panel.start_step = num_panel_steps - num_door_steps;
5252           door->panel.start_step_closing = door->panel.start_step;
5253         }
5254         else
5255         {
5256           num_panel_steps = door_rect->height / door->step_offset;
5257           door->panel.start_step = num_panel_steps - num_door_steps / 2;
5258           door->panel.start_step_closing = door->panel.start_step;
5259           door->panel.step_delay *= 2;
5260         }
5261       }
5262     }
5263   }
5264 }
5265
5266 void InitDoors(void)
5267 {
5268   int i;
5269
5270   for (i = 0; door_part_controls[i].door_token != -1; i++)
5271   {
5272     struct DoorPartControlInfo *dpc = &door_part_controls[i];
5273     struct DoorPartOrderInfo *dpo = &door_part_order[i];
5274
5275     // initialize "start_step_opening" and "start_step_closing", if needed
5276     if (dpc->pos->start_step_opening == 0 &&
5277         dpc->pos->start_step_closing == 0)
5278     {
5279       // dpc->pos->start_step_opening = dpc->pos->start_step;
5280       dpc->pos->start_step_closing = dpc->pos->start_step;
5281     }
5282
5283     // fill structure for door part draw order (sorted below)
5284     dpo->nr = i;
5285     dpo->sort_priority = dpc->pos->sort_priority;
5286   }
5287
5288   // sort door part controls according to sort_priority and graphic number
5289   qsort(door_part_order, MAX_DOOR_PARTS,
5290         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5291 }
5292
5293 unsigned int OpenDoor(unsigned int door_state)
5294 {
5295   if (door_state & DOOR_COPY_BACK)
5296   {
5297     if (door_state & DOOR_OPEN_1)
5298       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5299                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5300
5301     if (door_state & DOOR_OPEN_2)
5302       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5303                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5304
5305     door_state &= ~DOOR_COPY_BACK;
5306   }
5307
5308   return MoveDoor(door_state);
5309 }
5310
5311 unsigned int CloseDoor(unsigned int door_state)
5312 {
5313   unsigned int old_door_state = GetDoorState();
5314
5315   if (!(door_state & DOOR_NO_COPY_BACK))
5316   {
5317     if (old_door_state & DOOR_OPEN_1)
5318       BlitBitmap(backbuffer, bitmap_db_door_1,
5319                  DX, DY, DXSIZE, DYSIZE, 0, 0);
5320
5321     if (old_door_state & DOOR_OPEN_2)
5322       BlitBitmap(backbuffer, bitmap_db_door_2,
5323                  VX, VY, VXSIZE, VYSIZE, 0, 0);
5324
5325     door_state &= ~DOOR_NO_COPY_BACK;
5326   }
5327
5328   return MoveDoor(door_state);
5329 }
5330
5331 unsigned int GetDoorState(void)
5332 {
5333   return MoveDoor(DOOR_GET_STATE);
5334 }
5335
5336 unsigned int SetDoorState(unsigned int door_state)
5337 {
5338   return MoveDoor(door_state | DOOR_SET_STATE);
5339 }
5340
5341 static int euclid(int a, int b)
5342 {
5343   return (b ? euclid(b, a % b) : a);
5344 }
5345
5346 unsigned int MoveDoor(unsigned int door_state)
5347 {
5348   struct Rect door_rect_list[] =
5349   {
5350     { DX, DY, DXSIZE, DYSIZE },
5351     { VX, VY, VXSIZE, VYSIZE }
5352   };
5353   static int door1 = DOOR_CLOSE_1;
5354   static int door2 = DOOR_CLOSE_2;
5355   DelayCounter door_delay = { 0 };
5356   int i;
5357
5358   if (door_state == DOOR_GET_STATE)
5359     return (door1 | door2);
5360
5361   if (door_state & DOOR_SET_STATE)
5362   {
5363     if (door_state & DOOR_ACTION_1)
5364       door1 = door_state & DOOR_ACTION_1;
5365     if (door_state & DOOR_ACTION_2)
5366       door2 = door_state & DOOR_ACTION_2;
5367
5368     return (door1 | door2);
5369   }
5370
5371   if (!(door_state & DOOR_FORCE_REDRAW))
5372   {
5373     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5374       door_state &= ~DOOR_OPEN_1;
5375     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5376       door_state &= ~DOOR_CLOSE_1;
5377     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5378       door_state &= ~DOOR_OPEN_2;
5379     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5380       door_state &= ~DOOR_CLOSE_2;
5381   }
5382
5383   if (global.autoplay_leveldir)
5384   {
5385     door_state |= DOOR_NO_DELAY;
5386     door_state &= ~DOOR_CLOSE_ALL;
5387   }
5388
5389   if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5390     door_state |= DOOR_NO_DELAY;
5391
5392   if (door_state & DOOR_ACTION)
5393   {
5394     boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5395     boolean door_panel_drawn[NUM_DOORS];
5396     boolean panel_has_doors[NUM_DOORS];
5397     boolean door_part_skip[MAX_DOOR_PARTS];
5398     boolean door_part_done[MAX_DOOR_PARTS];
5399     boolean door_part_done_all;
5400     int num_steps[MAX_DOOR_PARTS];
5401     int max_move_delay = 0;     // delay for complete animations of all doors
5402     int max_step_delay = 0;     // delay (ms) between two animation frames
5403     int num_move_steps = 0;     // number of animation steps for all doors
5404     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
5405     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
5406     int start = 0;
5407     int k;
5408
5409     for (i = 0; i < NUM_DOORS; i++)
5410       panel_has_doors[i] = FALSE;
5411
5412     for (i = 0; i < MAX_DOOR_PARTS; i++)
5413     {
5414       struct DoorPartControlInfo *dpc = &door_part_controls[i];
5415       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5416       int door_token = dpc->door_token;
5417
5418       door_part_done[i] = FALSE;
5419       door_part_skip[i] = (!(door_state & door_token) ||
5420                            !g->bitmap);
5421     }
5422
5423     for (i = 0; i < MAX_DOOR_PARTS; i++)
5424     {
5425       int nr = door_part_order[i].nr;
5426       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5427       struct DoorPartPosInfo *pos = dpc->pos;
5428       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5429       int door_token = dpc->door_token;
5430       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5431       boolean is_panel = DOOR_PART_IS_PANEL(nr);
5432       int step_xoffset = ABS(pos->step_xoffset);
5433       int step_yoffset = ABS(pos->step_yoffset);
5434       int step_delay = pos->step_delay;
5435       int current_door_state = door_state & door_token;
5436       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5437       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5438       boolean part_opening = (is_panel ? door_closing : door_opening);
5439       int start_step = (part_opening ? pos->start_step_opening :
5440                         pos->start_step_closing);
5441       float move_xsize = (step_xoffset ? g->width  : 0);
5442       float move_ysize = (step_yoffset ? g->height : 0);
5443       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5444       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5445       int move_steps = (move_xsteps && move_ysteps ?
5446                         MIN(move_xsteps, move_ysteps) :
5447                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
5448       int move_delay = move_steps * step_delay;
5449
5450       if (door_part_skip[nr])
5451         continue;
5452
5453       max_move_delay = MAX(max_move_delay, move_delay);
5454       max_step_delay = (max_step_delay == 0 ? step_delay :
5455                         euclid(max_step_delay, step_delay));
5456       num_steps[nr] = move_steps;
5457
5458       if (!is_panel)
5459       {
5460         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5461
5462         panel_has_doors[door_index] = TRUE;
5463       }
5464     }
5465
5466     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
5467
5468     num_move_steps = max_move_delay / max_step_delay;
5469     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5470
5471     door_delay.value = max_step_delay;
5472
5473     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5474     {
5475       start = num_move_steps - 1;
5476     }
5477     else
5478     {
5479       // opening door sound has priority over simultaneously closing door
5480       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5481       {
5482         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5483
5484         if (door_state & DOOR_OPEN_1)
5485           PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5486         if (door_state & DOOR_OPEN_2)
5487           PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5488       }
5489       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5490       {
5491         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5492
5493         if (door_state & DOOR_CLOSE_1)
5494           PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5495         if (door_state & DOOR_CLOSE_2)
5496           PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5497       }
5498     }
5499
5500     for (k = start; k < num_move_steps; k++)
5501     {
5502       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
5503
5504       door_part_done_all = TRUE;
5505
5506       for (i = 0; i < NUM_DOORS; i++)
5507         door_panel_drawn[i] = FALSE;
5508
5509       for (i = 0; i < MAX_DOOR_PARTS; i++)
5510       {
5511         int nr = door_part_order[i].nr;
5512         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5513         struct DoorPartPosInfo *pos = dpc->pos;
5514         struct GraphicInfo *g = &graphic_info[dpc->graphic];
5515         int door_token = dpc->door_token;
5516         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5517         boolean is_panel = DOOR_PART_IS_PANEL(nr);
5518         boolean is_panel_and_door_has_closed = FALSE;
5519         struct Rect *door_rect = &door_rect_list[door_index];
5520         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5521                                   bitmap_db_door_2);
5522         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5523         int current_door_state = door_state & door_token;
5524         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5525         boolean door_closing = !door_opening;
5526         boolean part_opening = (is_panel ? door_closing : door_opening);
5527         boolean part_closing = !part_opening;
5528         int start_step = (part_opening ? pos->start_step_opening :
5529                           pos->start_step_closing);
5530         int step_delay = pos->step_delay;
5531         int step_factor = step_delay / max_step_delay;
5532         int k1 = (step_factor ? k / step_factor + 1 : k);
5533         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5534         int kk = MAX(0, k2);
5535         int g_src_x = 0;
5536         int g_src_y = 0;
5537         int src_x, src_y, src_xx, src_yy;
5538         int dst_x, dst_y, dst_xx, dst_yy;
5539         int width, height;
5540
5541         if (door_part_skip[nr])
5542           continue;
5543
5544         if (!(door_state & door_token))
5545           continue;
5546
5547         if (!g->bitmap)
5548           continue;
5549
5550         if (!is_panel)
5551         {
5552           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5553           int kk_door = MAX(0, k2_door);
5554           int sync_frame = kk_door * door_delay.value;
5555           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5556
5557           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5558                                 &g_src_x, &g_src_y);
5559         }
5560
5561         // draw door panel
5562
5563         if (!door_panel_drawn[door_index])
5564         {
5565           ClearRectangle(drawto, door_rect->x, door_rect->y,
5566                          door_rect->width, door_rect->height);
5567
5568           door_panel_drawn[door_index] = TRUE;
5569         }
5570
5571         // draw opening or closing door parts
5572
5573         if (pos->step_xoffset < 0)      // door part on right side
5574         {
5575           src_xx = 0;
5576           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5577           width = g->width;
5578
5579           if (dst_xx + width > door_rect->width)
5580             width = door_rect->width - dst_xx;
5581         }
5582         else                            // door part on left side
5583         {
5584           src_xx = 0;
5585           dst_xx = pos->x - kk * pos->step_xoffset;
5586
5587           if (dst_xx < 0)
5588           {
5589             src_xx = ABS(dst_xx);
5590             dst_xx = 0;
5591           }
5592
5593           width = g->width - src_xx;
5594
5595           if (width > door_rect->width)
5596             width = door_rect->width;
5597
5598           // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5599         }
5600
5601         if (pos->step_yoffset < 0)      // door part on bottom side
5602         {
5603           src_yy = 0;
5604           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5605           height = g->height;
5606
5607           if (dst_yy + height > door_rect->height)
5608             height = door_rect->height - dst_yy;
5609         }
5610         else                            // door part on top side
5611         {
5612           src_yy = 0;
5613           dst_yy = pos->y - kk * pos->step_yoffset;
5614
5615           if (dst_yy < 0)
5616           {
5617             src_yy = ABS(dst_yy);
5618             dst_yy = 0;
5619           }
5620
5621           height = g->height - src_yy;
5622         }
5623
5624         src_x = g_src_x + src_xx;
5625         src_y = g_src_y + src_yy;
5626
5627         dst_x = door_rect->x + dst_xx;
5628         dst_y = door_rect->y + dst_yy;
5629
5630         is_panel_and_door_has_closed =
5631           (is_panel &&
5632            door_closing &&
5633            panel_has_doors[door_index] &&
5634            k >= num_move_steps_doors_only - 1);
5635
5636         if (width  >= 0 && width  <= g->width &&
5637             height >= 0 && height <= g->height &&
5638             !is_panel_and_door_has_closed)
5639         {
5640           if (is_panel || !pos->draw_masked)
5641             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5642                        dst_x, dst_y);
5643           else
5644             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5645                              dst_x, dst_y);
5646         }
5647
5648         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5649
5650         if ((part_opening && (width < 0         || height < 0)) ||
5651             (part_closing && (width >= g->width && height >= g->height)))
5652           door_part_done[nr] = TRUE;
5653
5654         // continue door part animations, but not panel after door has closed
5655         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5656           door_part_done_all = FALSE;
5657       }
5658
5659       if (!(door_state & DOOR_NO_DELAY))
5660       {
5661         if (game_ended)
5662           HandleGameActions();
5663
5664         BackToFront();
5665
5666         SkipUntilDelayReached(&door_delay, &k, last_frame);
5667
5668         // prevent OS (Windows) from complaining about program not responding
5669         CheckQuitEvent();
5670       }
5671
5672       if (door_part_done_all)
5673         break;
5674     }
5675
5676     if (!(door_state & DOOR_NO_DELAY))
5677     {
5678       // wait for specified door action post delay
5679       if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5680         door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5681       else if (door_state & DOOR_ACTION_1)
5682         door_delay.value = door_1.post_delay;
5683       else if (door_state & DOOR_ACTION_2)
5684         door_delay.value = door_2.post_delay;
5685
5686       while (!DelayReached(&door_delay))
5687       {
5688         if (game_ended)
5689           HandleGameActions();
5690
5691         BackToFront();
5692       }
5693     }
5694   }
5695
5696   if (door_state & DOOR_ACTION_1)
5697     door1 = door_state & DOOR_ACTION_1;
5698   if (door_state & DOOR_ACTION_2)
5699     door2 = door_state & DOOR_ACTION_2;
5700
5701   // draw masked border over door area
5702   DrawMaskedBorder(REDRAW_DOOR_1);
5703   DrawMaskedBorder(REDRAW_DOOR_2);
5704
5705   ClearAutoRepeatKeyEvents();
5706
5707   return (door1 | door2);
5708 }
5709
5710 static boolean useSpecialEditorDoor(void)
5711 {
5712   int graphic = IMG_GLOBAL_BORDER_EDITOR;
5713   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5714
5715   // do not draw special editor door if editor border defined or redefined
5716   if (graphic_info[graphic].bitmap != NULL || redefined)
5717     return FALSE;
5718
5719   // do not draw special editor door if global border defined to be empty
5720   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5721     return FALSE;
5722
5723   // do not draw special editor door if viewport definitions do not match
5724   if (EX != VX ||
5725       EY >= VY ||
5726       EXSIZE != VXSIZE ||
5727       EY + EYSIZE != VY + VYSIZE)
5728     return FALSE;
5729
5730   return TRUE;
5731 }
5732
5733 void DrawSpecialEditorDoor(void)
5734 {
5735   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5736   int top_border_width = gfx1->width;
5737   int top_border_height = gfx1->height;
5738   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5739   int ex = EX - outer_border;
5740   int ey = EY - outer_border;
5741   int vy = VY - outer_border;
5742   int exsize = EXSIZE + 2 * outer_border;
5743
5744   if (!useSpecialEditorDoor())
5745     return;
5746
5747   // draw bigger level editor toolbox window
5748   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5749              top_border_width, top_border_height, ex, ey - top_border_height);
5750   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5751              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5752
5753   redraw_mask |= REDRAW_ALL;
5754 }
5755
5756 void UndrawSpecialEditorDoor(void)
5757 {
5758   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5759   int top_border_width = gfx1->width;
5760   int top_border_height = gfx1->height;
5761   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5762   int ex = EX - outer_border;
5763   int ey = EY - outer_border;
5764   int ey_top = ey - top_border_height;
5765   int exsize = EXSIZE + 2 * outer_border;
5766   int eysize = EYSIZE + 2 * outer_border;
5767
5768   if (!useSpecialEditorDoor())
5769     return;
5770
5771   // draw normal tape recorder window
5772   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5773   {
5774     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5775                ex, ey_top, top_border_width, top_border_height,
5776                ex, ey_top);
5777     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5778                ex, ey, exsize, eysize, ex, ey);
5779   }
5780   else
5781   {
5782     // if screen background is set to "[NONE]", clear editor toolbox window
5783     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5784     ClearRectangle(drawto, ex, ey, exsize, eysize);
5785   }
5786
5787   redraw_mask |= REDRAW_ALL;
5788 }
5789
5790
5791 // ---------- new tool button stuff -------------------------------------------
5792
5793 static struct
5794 {
5795   int graphic;
5796   struct TextPosInfo *pos;
5797   int gadget_id;
5798   boolean is_touch_button;
5799   char *infotext;
5800 } toolbutton_info[NUM_TOOL_BUTTONS] =
5801 {
5802   {
5803     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
5804     TOOL_CTRL_ID_YES, FALSE,            "yes"
5805   },
5806   {
5807     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5808     TOOL_CTRL_ID_NO, FALSE,             "no"
5809   },
5810   {
5811     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5812     TOOL_CTRL_ID_CONFIRM, FALSE,        "confirm"
5813   },
5814   {
5815     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5816     TOOL_CTRL_ID_PLAYER_1, FALSE,       "player 1"
5817   },
5818   {
5819     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5820     TOOL_CTRL_ID_PLAYER_2, FALSE,       "player 2"
5821   },
5822   {
5823     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5824     TOOL_CTRL_ID_PLAYER_3, FALSE,       "player 3"
5825   },
5826   {
5827     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5828     TOOL_CTRL_ID_PLAYER_4, FALSE,       "player 4"
5829   },
5830   {
5831     IMG_GFX_REQUEST_BUTTON_TOUCH_YES,   &request.button.touch_yes,
5832     TOOL_CTRL_ID_TOUCH_YES, TRUE,       "yes"
5833   },
5834   {
5835     IMG_GFX_REQUEST_BUTTON_TOUCH_NO,    &request.button.touch_no,
5836     TOOL_CTRL_ID_TOUCH_NO, TRUE,        "no"
5837   },
5838   {
5839     IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5840     TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE,   "confirm"
5841   }
5842 };
5843
5844 void CreateToolButtons(void)
5845 {
5846   int i;
5847
5848   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5849   {
5850     int graphic = toolbutton_info[i].graphic;
5851     struct GraphicInfo *gfx = &graphic_info[graphic];
5852     struct TextPosInfo *pos = toolbutton_info[i].pos;
5853     struct GadgetInfo *gi;
5854     Bitmap *deco_bitmap = None;
5855     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5856     unsigned int event_mask = GD_EVENT_RELEASED;
5857     boolean is_touch_button = toolbutton_info[i].is_touch_button;
5858     int base_x = (is_touch_button ? 0 : DX);
5859     int base_y = (is_touch_button ? 0 : DY);
5860     int gd_x = gfx->src_x;
5861     int gd_y = gfx->src_y;
5862     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5863     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5864     int x = pos->x;
5865     int y = pos->y;
5866     int id = i;
5867
5868     // do not use touch buttons if overlay touch buttons are disabled
5869     if (is_touch_button && !setup.touch.overlay_buttons)
5870       continue;
5871
5872     if (global.use_envelope_request && !is_touch_button)
5873     {
5874       setRequestPosition(&base_x, &base_y, TRUE);
5875
5876       // check if request buttons are outside of envelope and fix, if needed
5877       if (x < 0 || x + gfx->width  > request.width ||
5878           y < 0 || y + gfx->height > request.height)
5879       {
5880         if (id == TOOL_CTRL_ID_YES)
5881         {
5882           x = 0;
5883           y = request.height - 2 * request.border_size - gfx->height;
5884         }
5885         else if (id == TOOL_CTRL_ID_NO)
5886         {
5887           x = request.width  - 2 * request.border_size - gfx->width;
5888           y = request.height - 2 * request.border_size - gfx->height;
5889         }
5890         else if (id == TOOL_CTRL_ID_CONFIRM)
5891         {
5892           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5893           y = request.height - 2 * request.border_size - gfx->height;
5894         }
5895         else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5896         {
5897           int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5898
5899           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5900           y = request.height - 2 * request.border_size - gfx->height * 2;
5901
5902           x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5903           y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5904         }
5905       }
5906     }
5907
5908     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5909         pos->draw_player)
5910     {
5911       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5912
5913       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5914                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5915       deco_xpos = (gfx->width  - pos->size) / 2;
5916       deco_ypos = (gfx->height - pos->size) / 2;
5917     }
5918
5919     gi = CreateGadget(GDI_CUSTOM_ID, id,
5920                       GDI_IMAGE_ID, graphic,
5921                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5922                       GDI_X, base_x + x,
5923                       GDI_Y, base_y + y,
5924                       GDI_WIDTH, gfx->width,
5925                       GDI_HEIGHT, gfx->height,
5926                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5927                       GDI_STATE, GD_BUTTON_UNPRESSED,
5928                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5929                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5930                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5931                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5932                       GDI_DECORATION_SIZE, pos->size, pos->size,
5933                       GDI_DECORATION_SHIFTING, 1, 1,
5934                       GDI_DIRECT_DRAW, FALSE,
5935                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5936                       GDI_EVENT_MASK, event_mask,
5937                       GDI_CALLBACK_ACTION, HandleToolButtons,
5938                       GDI_END);
5939
5940     if (gi == NULL)
5941       Fail("cannot create gadget");
5942
5943     tool_gadget[id] = gi;
5944   }
5945 }
5946
5947 void FreeToolButtons(void)
5948 {
5949   int i;
5950
5951   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5952     FreeGadget(tool_gadget[i]);
5953 }
5954
5955 static void MapToolButtons(unsigned int req_state)
5956 {
5957   if (req_state & REQ_ASK)
5958   {
5959     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5960     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5961     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5962     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5963   }
5964   else if (req_state & REQ_CONFIRM)
5965   {
5966     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5967     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5968   }
5969   else if (req_state & REQ_PLAYER)
5970   {
5971     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5972     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5973     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5974     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5975   }
5976 }
5977
5978 static void UnmapToolButtons(void)
5979 {
5980   int i;
5981
5982   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5983     UnmapGadget(tool_gadget[i]);
5984 }
5985
5986 static void HandleToolButtons(struct GadgetInfo *gi)
5987 {
5988   request_gadget_id = gi->custom_id;
5989 }
5990
5991 static struct Mapping_BD_to_RND_object
5992 {
5993   int element_bd;
5994   boolean is_rnd_to_bd_mapping;         // unique mapping BD <-> RND
5995
5996   int element_rnd;
5997   int action;
5998   int direction;
5999 }
6000 bd_object_mapping_list[] =
6001 {
6002   // additional RND style elements mapped to BD style elements must be listed first
6003
6004   {
6005     O_DIRT,                                     TRUE,
6006     EL_SAND,                                    -1, -1
6007   },
6008   {
6009     O_STONE,                                    TRUE,
6010     EL_BD_ROCK,                                 -1, -1
6011   },
6012   {
6013     O_BRICK,                                    TRUE,
6014     EL_BD_WALL,                                 -1, -1
6015   },
6016   {
6017     O_STEEL,                                    TRUE,
6018     EL_STEELWALL,                               -1, -1
6019   },
6020   {
6021     O_DIAMOND,                                  TRUE,
6022     EL_BD_DIAMOND,                              -1, -1
6023   },
6024   {
6025     O_INBOX,                                    TRUE,
6026     EL_PLAYER_1,                                -1, -1
6027   },
6028   {
6029     O_INBOX,                                    TRUE,
6030     EL_PLAYER_2,                                -1, -1
6031   },
6032   {
6033     O_INBOX,                                    TRUE,
6034     EL_PLAYER_3,                                -1, -1
6035   },
6036   {
6037     O_INBOX,                                    TRUE,
6038     EL_PLAYER_4,                                -1, -1
6039   },
6040   {
6041     O_PRE_OUTBOX,                               TRUE,
6042     EL_EXIT_CLOSED,                             -1, -1
6043   },
6044
6045   // BD style elements with their corresponding RND style elements
6046
6047   {
6048     O_SPACE,                                    TRUE,
6049     EL_EMPTY,                                   -1, -1
6050   },
6051   {
6052     O_DIRT,                                     TRUE,
6053     EL_BD_SAND,                                 -1, -1
6054   },
6055   {
6056     O_DIRT_SLOPED_UP_RIGHT,                     TRUE,
6057     EL_BD_SAND_SLOPED_UP_RIGHT,                 -1, -1
6058   },
6059   {
6060     O_DIRT_SLOPED_UP_LEFT,                      TRUE,
6061     EL_BD_SAND_SLOPED_UP_LEFT,                  -1, -1
6062   },
6063   {
6064     O_DIRT_SLOPED_DOWN_LEFT,                    TRUE,
6065     EL_BD_SAND_SLOPED_DOWN_LEFT,                -1, -1
6066   },
6067   {
6068     O_DIRT_SLOPED_DOWN_RIGHT,                   TRUE,
6069     EL_BD_SAND_SLOPED_DOWN_RIGHT,               -1, -1
6070   },
6071   {
6072     O_DIRT_BALL,                                TRUE,
6073     EL_BD_SAND_BALL,                            -1, -1
6074   },
6075   {
6076     O_DIRT_BALL_F,                              FALSE,
6077     EL_BD_SAND_BALL,                            ACTION_FALLING, -1
6078   },
6079   {
6080     O_DIRT_LOOSE,                               TRUE,
6081     EL_BD_SAND_LOOSE,                           -1, -1
6082   },
6083   {
6084     O_DIRT_LOOSE_F,                             FALSE,
6085     EL_BD_SAND_LOOSE,                           ACTION_FALLING, -1
6086   },
6087   {
6088     O_DIRT2,                                    TRUE,
6089     EL_BD_SAND_2,                               -1, -1
6090   },
6091   {
6092     O_BRICK,                                    TRUE,
6093     EL_BD_WALL,                                 -1, -1
6094   },
6095   {
6096     O_BRICK_SLOPED_UP_RIGHT,                    TRUE,
6097     EL_BD_WALL_SLOPED_UP_RIGHT,                 -1, -1
6098   },
6099   {
6100     O_BRICK_SLOPED_UP_LEFT,                     TRUE,
6101     EL_BD_WALL_SLOPED_UP_LEFT,                  -1, -1
6102   },
6103   {
6104     O_BRICK_SLOPED_DOWN_LEFT,                   TRUE,
6105     EL_BD_WALL_SLOPED_DOWN_LEFT,                -1, -1
6106   },
6107   {
6108     O_BRICK_SLOPED_DOWN_RIGHT,                  TRUE,
6109     EL_BD_WALL_SLOPED_DOWN_RIGHT,               -1, -1
6110   },
6111   {
6112     O_BRICK_NON_SLOPED,                         TRUE,
6113     EL_BD_WALL_NON_SLOPED,                      -1, -1
6114   },
6115   {
6116     O_MAGIC_WALL,                               TRUE,
6117     EL_BD_MAGIC_WALL,                           ACTION_ACTIVE, -1
6118   },
6119   {
6120     O_PRE_OUTBOX,                               TRUE,
6121     EL_BD_EXIT_CLOSED,                          -1, -1
6122   },
6123   {
6124     O_OUTBOX,                                   TRUE,
6125     EL_BD_EXIT_OPEN,                            -1, -1
6126   },
6127   {
6128     O_PRE_INVIS_OUTBOX,                         TRUE,
6129     EL_BD_INVISIBLE_EXIT_CLOSED,                -1, -1
6130   },
6131   {
6132     O_INVIS_OUTBOX,                             TRUE,
6133     EL_BD_INVISIBLE_EXIT_OPEN,                  -1, -1
6134   },
6135   {
6136     O_STEEL,                                    TRUE,
6137     EL_BD_STEELWALL,                            -1, -1
6138   },
6139   {
6140     O_STEEL_SLOPED_UP_RIGHT,                    TRUE,
6141     EL_BD_STEELWALL_SLOPED_UP_RIGHT,            -1, -1
6142   },
6143   {
6144     O_STEEL_SLOPED_UP_LEFT,                     TRUE,
6145     EL_BD_STEELWALL_SLOPED_UP_LEFT,             -1, -1
6146   },
6147   {
6148     O_STEEL_SLOPED_DOWN_LEFT,                   TRUE,
6149     EL_BD_STEELWALL_SLOPED_DOWN_LEFT,           -1, -1
6150   },
6151   {
6152     O_STEEL_SLOPED_DOWN_RIGHT,                  TRUE,
6153     EL_BD_STEELWALL_SLOPED_DOWN_RIGHT,          -1, -1
6154   },
6155   {
6156     O_STEEL_EXPLODABLE,                         TRUE,
6157     EL_BD_STEELWALL_EXPLODABLE,                 -1, -1
6158   },
6159   {
6160     O_STEEL_EATABLE,                            TRUE,
6161     EL_BD_STEELWALL_DIGGABLE,                   -1, -1
6162   },
6163   {
6164     O_BRICK_EATABLE,                            TRUE,
6165     EL_BD_WALL_DIGGABLE,                        -1, -1
6166   },
6167   {
6168     O_STONE,                                    TRUE,
6169     EL_BD_ROCK,                                 -1, -1
6170   },
6171   {
6172     O_STONE_F,                                  FALSE,
6173     EL_BD_ROCK,                                 ACTION_FALLING, -1
6174   },
6175   {
6176     O_FLYING_STONE,                             TRUE,
6177     EL_BD_FLYING_ROCK,                          -1, -1
6178   },
6179   {
6180     O_FLYING_STONE_F,                           FALSE,
6181     EL_BD_FLYING_ROCK,                          ACTION_FALLING, -1
6182   },
6183   {
6184     O_MEGA_STONE,                               TRUE,
6185     EL_BD_MEGA_ROCK,                            -1, -1
6186   },
6187   {
6188     O_MEGA_STONE_F,                             FALSE,
6189     EL_BD_MEGA_ROCK,                            ACTION_FALLING, -1
6190   },
6191   {
6192     O_DIAMOND,                                  TRUE,
6193     EL_BD_DIAMOND,                              -1, -1
6194   },
6195   {
6196     O_DIAMOND_F,                                FALSE,
6197     EL_BD_DIAMOND,                              ACTION_FALLING, -1
6198   },
6199   {
6200     O_FLYING_DIAMOND,                           TRUE,
6201     EL_BD_FLYING_DIAMOND,                       -1, -1
6202   },
6203   {
6204     O_FLYING_DIAMOND_F,                         FALSE,
6205     EL_BD_FLYING_DIAMOND,                       ACTION_FALLING, -1
6206   },
6207   {
6208     O_NUT,                                      TRUE,
6209     EL_BD_NUT,                                  -1, -1
6210   },
6211   {
6212     O_NUT_F,                                    FALSE,
6213     EL_BD_NUT,                                  ACTION_FALLING, -1
6214   },
6215   {
6216     O_BLADDER_SPENDER,                          TRUE,
6217     EL_BD_BLADDER_SPENDER,                      -1, -1
6218   },
6219   {
6220     O_INBOX,                                    TRUE,
6221     EL_BD_INBOX,                                -1, -1
6222   },
6223   {
6224     O_H_EXPANDING_WALL,                         TRUE,
6225     EL_BD_EXPANDABLE_WALL_HORIZONTAL,           -1, -1
6226   },
6227   {
6228     O_V_EXPANDING_WALL,                         TRUE,
6229     EL_BD_EXPANDABLE_WALL_VERTICAL,             -1, -1
6230   },
6231   {
6232     O_EXPANDING_WALL,                           TRUE,
6233     EL_BD_EXPANDABLE_WALL_ANY,                  -1, -1
6234   },
6235   {
6236     O_H_EXPANDING_STEEL_WALL,                   TRUE,
6237     EL_BD_EXPANDABLE_STEELWALL_HORIZONTAL,      -1, -1
6238   },
6239   {
6240     O_V_EXPANDING_STEEL_WALL,                   TRUE,
6241     EL_BD_EXPANDABLE_STEELWALL_VERTICAL,        -1, -1
6242   },
6243   {
6244     O_EXPANDING_STEEL_WALL,                     TRUE,
6245     EL_BD_EXPANDABLE_STEELWALL_ANY,             -1, -1
6246   },
6247   {
6248     O_EXPANDING_WALL_SWITCH,                    TRUE,
6249     EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL,    -1, -1
6250   },
6251   {
6252     O_CREATURE_SWITCH,                          TRUE,
6253     EL_BD_CREATURE_SWITCH,                      -1, -1
6254   },
6255   {
6256     O_BITER_SWITCH,                             TRUE,
6257     EL_BD_BITER_SWITCH_1,                       -1, -1
6258   },
6259   {
6260     O_REPLICATOR_SWITCH,                        TRUE,
6261     EL_BD_REPLICATOR_SWITCH,                    -1, -1
6262   },
6263   {
6264     O_CONVEYOR_SWITCH,                          TRUE,
6265     EL_BD_CONVEYOR_SWITCH,                      -1, -1
6266   },
6267   {
6268     O_CONVEYOR_DIR_SWITCH,                      TRUE,
6269     EL_BD_CONVEYOR_DIR_SWITCH_RIGHT,            -1, -1
6270   },
6271   {
6272     O_ACID,                                     TRUE,
6273     EL_BD_ACID,                                 -1, -1
6274   },
6275   {
6276     O_FALLING_WALL,                             TRUE,
6277     EL_BD_FALLING_WALL,                         -1, -1
6278   },
6279   {
6280     O_FALLING_WALL_F,                           FALSE,
6281     EL_BD_FALLING_WALL,                         ACTION_FALLING, -1
6282   },
6283   {
6284     O_BOX,                                      TRUE,
6285     EL_BD_BOX,                                  -1, -1
6286   },
6287   {
6288     O_TIME_PENALTY,                             TRUE,
6289     EL_BD_TIME_PENALTY,                         -1, -1
6290   },
6291   {
6292     O_GRAVESTONE,                               TRUE,
6293     EL_BD_GRAVESTONE,                           -1, -1
6294   },
6295   {
6296     O_STONE_GLUED,                              TRUE,
6297     EL_BD_ROCK_GLUED,                           -1, -1
6298   },
6299   {
6300     O_DIAMOND_GLUED,                            TRUE,
6301     EL_BD_DIAMOND_GLUED,                        -1, -1
6302   },
6303   {
6304     O_DIAMOND_KEY,                              TRUE,
6305     EL_BD_DIAMOND_KEY,                          -1, -1
6306   },
6307   {
6308     O_TRAPPED_DIAMOND,                          TRUE,
6309     EL_BD_TRAPPED_DIAMOND,                      -1, -1
6310   },
6311   {
6312     O_CLOCK,                                    TRUE,
6313     EL_BD_CLOCK,                                -1, -1
6314   },
6315   {
6316     O_DIRT_GLUED,                               TRUE,
6317     EL_BD_SAND_GLUED,                           -1, -1
6318   },
6319   {
6320     O_KEY_1,                                    TRUE,
6321     EL_BD_KEY_1,                                -1, -1
6322   },
6323   {
6324     O_KEY_2,                                    TRUE,
6325     EL_BD_KEY_2,                                -1, -1
6326   },
6327   {
6328     O_KEY_3,                                    TRUE,
6329     EL_BD_KEY_3,                                -1, -1
6330   },
6331   {
6332     O_DOOR_1,                                   TRUE,
6333     EL_BD_GATE_1,                               -1, -1
6334   },
6335   {
6336     O_DOOR_2,                                   TRUE,
6337     EL_BD_GATE_2,                               -1, -1
6338   },
6339   {
6340     O_DOOR_3,                                   TRUE,
6341     EL_BD_GATE_3,                               -1, -1
6342   },
6343   {
6344     O_POT,                                      TRUE,
6345     EL_BD_POT,                                  -1, -1
6346   },
6347   {
6348     O_GRAVITY_SWITCH,                           TRUE,
6349     EL_BD_GRAVITY_SWITCH,                       -1, -1
6350   },
6351   {
6352     O_PNEUMATIC_HAMMER,                         TRUE,
6353     EL_BD_PNEUMATIC_HAMMER,                     -1, -1
6354   },
6355   {
6356     O_TELEPORTER,                               TRUE,
6357     EL_BD_TELEPORTER,                           -1, -1
6358   },
6359   {
6360     O_SKELETON,                                 TRUE,
6361     EL_BD_SKELETON,                             -1, -1
6362   },
6363   {
6364     O_WATER,                                    TRUE,
6365     EL_BD_WATER,                                -1, -1
6366   },
6367   {
6368     O_WATER_1,                                  FALSE,
6369     EL_BD_WATER,                                -1, -1
6370   },
6371   {
6372     O_WATER_2,                                  FALSE,
6373     EL_BD_WATER,                                -1, -1
6374   },
6375   {
6376     O_WATER_3,                                  FALSE,
6377     EL_BD_WATER,                                -1, -1
6378   },
6379   {
6380     O_WATER_4,                                  FALSE,
6381     EL_BD_WATER,                                -1, -1
6382   },
6383   {
6384     O_WATER_5,                                  FALSE,
6385     EL_BD_WATER,                                -1, -1
6386   },
6387   {
6388     O_WATER_6,                                  FALSE,
6389     EL_BD_WATER,                                -1, -1
6390   },
6391   {
6392     O_WATER_7,                                  FALSE,
6393     EL_BD_WATER,                                -1, -1
6394   },
6395   {
6396     O_WATER_8,                                  FALSE,
6397     EL_BD_WATER,                                -1, -1
6398   },
6399   {
6400     O_WATER_9,                                  FALSE,
6401     EL_BD_WATER,                                -1, -1
6402   },
6403   {
6404     O_WATER_10,                                 FALSE,
6405     EL_BD_WATER,                                -1, -1
6406   },
6407   {
6408     O_WATER_11,                                 FALSE,
6409     EL_BD_WATER,                                -1, -1
6410   },
6411   {
6412     O_WATER_12,                                 FALSE,
6413     EL_BD_WATER,                                -1, -1
6414   },
6415   {
6416     O_WATER_13,                                 FALSE,
6417     EL_BD_WATER,                                -1, -1
6418   },
6419   {
6420     O_WATER_14,                                 FALSE,
6421     EL_BD_WATER,                                -1, -1
6422   },
6423   {
6424     O_WATER_15,                                 FALSE,
6425     EL_BD_WATER,                                -1, -1
6426   },
6427   {
6428     O_WATER_16,                                 FALSE,
6429     EL_BD_WATER,                                -1, -1
6430   },
6431   {
6432     O_COW_1,                                    TRUE,
6433     EL_BD_COW_LEFT,                             -1, -1
6434   },
6435   {
6436     O_COW_2,                                    TRUE,
6437     EL_BD_COW_UP,                               -1, -1
6438   },
6439   {
6440     O_COW_3,                                    TRUE,
6441     EL_BD_COW_RIGHT,                            -1, -1
6442   },
6443   {
6444     O_COW_4,                                    TRUE,
6445     EL_BD_COW_DOWN,                             -1, -1
6446   },
6447   {
6448     O_COW_ENCLOSED_1,                           FALSE,
6449     EL_BD_COW_DOWN,                             -1, -1
6450   },
6451   {
6452     O_COW_ENCLOSED_2,                           FALSE,
6453     EL_BD_COW_DOWN,                             -1, -1
6454   },
6455   {
6456     O_COW_ENCLOSED_3,                           FALSE,
6457     EL_BD_COW_DOWN,                             -1, -1
6458   },
6459   {
6460     O_COW_ENCLOSED_4,                           FALSE,
6461     EL_BD_COW_DOWN,                             -1, -1
6462   },
6463   {
6464     O_COW_ENCLOSED_5,                           FALSE,
6465     EL_BD_COW_DOWN,                             -1, -1
6466   },
6467   {
6468     O_COW_ENCLOSED_6,                           FALSE,
6469     EL_BD_COW_DOWN,                             -1, -1
6470   },
6471   {
6472     O_COW_ENCLOSED_7,                           FALSE,
6473     EL_BD_COW_DOWN,                             -1, -1
6474   },
6475   {
6476     O_WALLED_DIAMOND,                           TRUE,
6477     EL_BD_WALL_DIAMOND,                         -1, -1
6478   },
6479   {
6480     O_WALLED_KEY_1,                             TRUE,
6481     EL_BD_WALL_KEY_1,                           -1, -1
6482   },
6483   {
6484     O_WALLED_KEY_2,                             TRUE,
6485     EL_BD_WALL_KEY_2,                           -1, -1
6486   },
6487   {
6488     O_WALLED_KEY_3,                             TRUE,
6489     EL_BD_WALL_KEY_3,                           -1, -1
6490   },
6491   {
6492     O_AMOEBA,                                   TRUE,
6493     EL_BD_AMOEBA,                               -1, -1
6494   },
6495   {
6496     O_AMOEBA_2,                                 TRUE,
6497     EL_BD_AMOEBA_2,                             -1, -1
6498   },
6499   {
6500     O_REPLICATOR,                               TRUE,
6501     EL_BD_REPLICATOR,                           -1, -1
6502   },
6503   {
6504     O_CONVEYOR_LEFT,                            TRUE,
6505     EL_BD_CONVEYOR_LEFT,                        -1, -1
6506   },
6507   {
6508     O_CONVEYOR_RIGHT,                           TRUE,
6509     EL_BD_CONVEYOR_RIGHT,                       -1, -1
6510   },
6511   {
6512     O_LAVA,                                     TRUE,
6513     EL_BD_LAVA,                                 -1, -1
6514   },
6515   {
6516     O_SWEET,                                    TRUE,
6517     EL_BD_SWEET,                                -1, -1
6518   },
6519   {
6520     O_VOODOO,                                   TRUE,
6521     EL_BD_VOODOO_DOLL,                          -1, -1
6522   },
6523   {
6524     O_SLIME,                                    TRUE,
6525     EL_BD_SLIME,                                -1, -1
6526   },
6527   {
6528     O_BLADDER,                                  TRUE,
6529     EL_BD_BLADDER,                              -1, -1
6530   },
6531   {
6532     O_BLADDER_1,                                FALSE,
6533     EL_BD_BLADDER,                              -1, -1
6534   },
6535   {
6536     O_BLADDER_2,                                FALSE,
6537     EL_BD_BLADDER,                              -1, -1
6538   },
6539   {
6540     O_BLADDER_3,                                FALSE,
6541     EL_BD_BLADDER,                              -1, -1
6542   },
6543   {
6544     O_BLADDER_4,                                FALSE,
6545     EL_BD_BLADDER,                              -1, -1
6546   },
6547   {
6548     O_BLADDER_5,                                FALSE,
6549     EL_BD_BLADDER,                              -1, -1
6550   },
6551   {
6552     O_BLADDER_6,                                FALSE,
6553     EL_BD_BLADDER,                              -1, -1
6554   },
6555   {
6556     O_BLADDER_7,                                FALSE,
6557     EL_BD_BLADDER,                              -1, -1
6558   },
6559   {
6560     O_BLADDER_8,                                FALSE,
6561     EL_BD_BLADDER,                              -1, -1
6562   },
6563   {
6564     O_WAITING_STONE,                            TRUE,
6565     EL_BD_WAITING_ROCK,                         -1, -1
6566   },
6567   {
6568     O_CHASING_STONE,                            TRUE,
6569     EL_BD_CHASING_ROCK,                         -1, -1
6570   },
6571   {
6572     O_GHOST,                                    TRUE,
6573     EL_BD_GHOST,                                -1, -1
6574   },
6575   {
6576     O_FIREFLY_1,                                TRUE,
6577     EL_BD_FIREFLY_LEFT,                         -1, -1
6578   },
6579   {
6580     O_FIREFLY_2,                                TRUE,
6581     EL_BD_FIREFLY_UP,                           -1, -1
6582   },
6583   {
6584     O_FIREFLY_3,                                TRUE,
6585     EL_BD_FIREFLY_RIGHT,                        -1, -1
6586   },
6587   {
6588     O_FIREFLY_4,                                TRUE,
6589     EL_BD_FIREFLY_DOWN,                         -1, -1
6590   },
6591   {
6592     O_ALT_FIREFLY_1,                            TRUE,
6593     EL_BD_FIREFLY_2_LEFT,                       -1, -1
6594   },
6595   {
6596     O_ALT_FIREFLY_2,                            TRUE,
6597     EL_BD_FIREFLY_2_UP,                         -1, -1
6598   },
6599   {
6600     O_ALT_FIREFLY_3,                            TRUE,
6601     EL_BD_FIREFLY_2_RIGHT,                      -1, -1
6602   },
6603   {
6604     O_ALT_FIREFLY_4,                            TRUE,
6605     EL_BD_FIREFLY_2_DOWN,                       -1, -1
6606   },
6607   {
6608     O_BUTTER_1,                                 TRUE,
6609     EL_BD_BUTTERFLY_LEFT,                       -1, -1
6610   },
6611   {
6612     O_BUTTER_2,                                 TRUE,
6613     EL_BD_BUTTERFLY_UP,                         -1, -1
6614   },
6615   {
6616     O_BUTTER_3,                                 TRUE,
6617     EL_BD_BUTTERFLY_RIGHT,                      -1, -1
6618   },
6619   {
6620     O_BUTTER_4,                                 TRUE,
6621     EL_BD_BUTTERFLY_DOWN,                       -1, -1
6622   },
6623   {
6624     O_ALT_BUTTER_1,                             TRUE,
6625     EL_BD_BUTTERFLY_2_LEFT,                     -1, -1
6626   },
6627   {
6628     O_ALT_BUTTER_2,                             TRUE,
6629     EL_BD_BUTTERFLY_2_UP,                       -1, -1
6630   },
6631   {
6632     O_ALT_BUTTER_3,                             TRUE,
6633     EL_BD_BUTTERFLY_2_RIGHT,                    -1, -1
6634   },
6635   {
6636     O_ALT_BUTTER_4,                             TRUE,
6637     EL_BD_BUTTERFLY_2_DOWN,                     -1, -1
6638   },
6639   {
6640     O_STONEFLY_1,                               TRUE,
6641     EL_BD_STONEFLY_LEFT,                        -1, -1
6642   },
6643   {
6644     O_STONEFLY_2,                               TRUE,
6645     EL_BD_STONEFLY_UP,                          -1, -1
6646   },
6647   {
6648     O_STONEFLY_3,                               TRUE,
6649     EL_BD_STONEFLY_RIGHT,                       -1, -1
6650   },
6651   {
6652     O_STONEFLY_4,                               TRUE,
6653     EL_BD_STONEFLY_DOWN,                        -1, -1
6654   },
6655   {
6656     O_BITER_1,                                  TRUE,
6657     EL_BD_BITER_UP,                             -1, -1
6658   },
6659   {
6660     O_BITER_2,                                  TRUE,
6661     EL_BD_BITER_RIGHT,                          -1, -1
6662   },
6663   {
6664     O_BITER_3,                                  TRUE,
6665     EL_BD_BITER_DOWN,                           -1, -1
6666   },
6667   {
6668     O_BITER_4,                                  TRUE,
6669     EL_BD_BITER_LEFT,                           -1, -1
6670   },
6671   {
6672     O_DRAGONFLY_1,                              TRUE,
6673     EL_BD_DRAGONFLY_LEFT,                       -1, -1
6674   },
6675   {
6676     O_DRAGONFLY_2,                              TRUE,
6677     EL_BD_DRAGONFLY_UP,                         -1, -1
6678   },
6679   {
6680     O_DRAGONFLY_3,                              TRUE,
6681     EL_BD_DRAGONFLY_RIGHT,                      -1, -1
6682   },
6683   {
6684     O_DRAGONFLY_4,                              TRUE,
6685     EL_BD_DRAGONFLY_DOWN,                       -1, -1
6686   },
6687   {
6688     O_PRE_PL_1,                                 FALSE,
6689     EL_BD_PLAYER,                               ACTION_GROWING, -1
6690   },
6691   {
6692     O_PRE_PL_2,                                 FALSE,
6693     EL_BD_PLAYER,                               ACTION_GROWING, -1
6694   },
6695   {
6696     O_PRE_PL_3,                                 FALSE,
6697     EL_BD_PLAYER,                               ACTION_GROWING, -1
6698   },
6699   {
6700     O_PLAYER,                                   TRUE,
6701     EL_BD_PLAYER,                               -1, -1
6702   },
6703   {
6704     O_PLAYER_BOMB,                              TRUE,
6705     EL_BD_PLAYER_WITH_BOMB,                     -1, -1
6706   },
6707   {
6708     O_PLAYER_GLUED,                             TRUE,
6709     EL_BD_PLAYER_GLUED,                         -1, -1
6710   },
6711   {
6712     O_PLAYER_STIRRING,                          TRUE,
6713     EL_BD_PLAYER_STIRRING,                      -1, -1
6714   },
6715   {
6716     O_BOMB,                                     TRUE,
6717     EL_BD_BOMB,                                 -1, -1
6718   },
6719   {
6720     O_BOMB_TICK_1,                              FALSE,
6721     EL_BD_BOMB,                                 ACTION_ACTIVE, -1
6722   },
6723   {
6724     O_BOMB_TICK_2,                              FALSE,
6725     EL_BD_BOMB,                                 ACTION_ACTIVE, -1
6726   },
6727   {
6728     O_BOMB_TICK_3,                              FALSE,
6729     EL_BD_BOMB,                                 ACTION_ACTIVE, -1
6730   },
6731   {
6732     O_BOMB_TICK_4,                              FALSE,
6733     EL_BD_BOMB,                                 ACTION_ACTIVE, -1
6734   },
6735   {
6736     O_BOMB_TICK_5,                              FALSE,
6737     EL_BD_BOMB,                                 ACTION_ACTIVE, -1
6738   },
6739   {
6740     O_BOMB_TICK_6,                              FALSE,
6741     EL_BD_BOMB,                                 ACTION_ACTIVE, -1
6742   },
6743   {
6744     O_BOMB_TICK_7,                              FALSE,
6745     EL_BD_BOMB,                                 ACTION_ACTIVE, -1
6746   },
6747   {
6748     O_NITRO_PACK,                               TRUE,
6749     EL_BD_NITRO_PACK,                           -1, -1
6750   },
6751   {
6752     O_NITRO_PACK_F,                             FALSE,
6753     EL_BD_NITRO_PACK,                           ACTION_FALLING, -1
6754   },
6755   {
6756     O_NITRO_PACK_EXPLODE,                       FALSE,
6757     EL_BD_NITRO_PACK,                           ACTION_EXPLODING, -1
6758   },
6759   {
6760     O_PRE_CLOCK_1,                              FALSE,
6761     EL_BD_CLOCK,                                ACTION_GROWING, -1
6762   },
6763   {
6764     O_PRE_CLOCK_2,                              FALSE,
6765     EL_BD_CLOCK,                                ACTION_GROWING, -1
6766   },
6767   {
6768     O_PRE_CLOCK_3,                              FALSE,
6769     EL_BD_CLOCK,                                ACTION_GROWING, -1
6770   },
6771   {
6772     O_PRE_CLOCK_4,                              FALSE,
6773     EL_BD_CLOCK,                                ACTION_GROWING, -1
6774   },
6775   {
6776     O_PRE_DIA_1,                                FALSE,
6777     EL_BD_DIAMOND,                              ACTION_GROWING, -1
6778   },
6779   {
6780     O_PRE_DIA_2,                                FALSE,
6781     EL_BD_DIAMOND,                              ACTION_GROWING, -1
6782   },
6783   {
6784     O_PRE_DIA_3,                                FALSE,
6785     EL_BD_DIAMOND,                              ACTION_GROWING, -1
6786   },
6787   {
6788     O_PRE_DIA_4,                                FALSE,
6789     EL_BD_DIAMOND,                              ACTION_GROWING, -1
6790   },
6791   {
6792     O_PRE_DIA_5,                                FALSE,
6793     EL_BD_DIAMOND,                              ACTION_GROWING, -1
6794   },
6795   {
6796     O_EXPLODE_1,                                FALSE,
6797     EL_DEFAULT,                                 ACTION_EXPLODING, -1
6798   },
6799   {
6800     O_EXPLODE_2,                                FALSE,
6801     EL_DEFAULT,                                 ACTION_EXPLODING, -1
6802   },
6803   {
6804     O_EXPLODE_3,                                FALSE,
6805     EL_DEFAULT,                                 ACTION_EXPLODING, -1
6806   },
6807   {
6808     O_EXPLODE_4,                                FALSE,
6809     EL_DEFAULT,                                 ACTION_EXPLODING, -1
6810   },
6811   {
6812     O_EXPLODE_5,                                FALSE,
6813     EL_DEFAULT,                                 ACTION_EXPLODING, -1
6814   },
6815   {
6816     O_PRE_STONE_1,                              FALSE,
6817     EL_BD_ROCK,                                 ACTION_GROWING, -1
6818   },
6819   {
6820     O_PRE_STONE_2,                              FALSE,
6821     EL_BD_ROCK,                                 ACTION_GROWING, -1
6822   },
6823   {
6824     O_PRE_STONE_3,                              FALSE,
6825     EL_BD_ROCK,                                 ACTION_GROWING, -1
6826   },
6827   {
6828     O_PRE_STONE_4,                              FALSE,
6829     EL_BD_ROCK,                                 ACTION_GROWING, -1
6830   },
6831   {
6832     O_PRE_STEEL_1,                              FALSE,
6833     EL_BD_STEELWALL,                            ACTION_GROWING, -1
6834   },
6835   {
6836     O_PRE_STEEL_2,                              FALSE,
6837     EL_BD_STEELWALL,                            ACTION_GROWING, -1
6838   },
6839   {
6840     O_PRE_STEEL_3,                              FALSE,
6841     EL_BD_STEELWALL,                            ACTION_GROWING, -1
6842   },
6843   {
6844     O_PRE_STEEL_4,                              FALSE,
6845     EL_BD_STEELWALL,                            ACTION_GROWING, -1
6846   },
6847   {
6848     O_GHOST_EXPL_1,                             FALSE,
6849     EL_BD_GHOST,                                ACTION_EXPLODING, -1
6850   },
6851   {
6852     O_GHOST_EXPL_2,                             FALSE,
6853     EL_BD_GHOST,                                ACTION_EXPLODING, -1
6854   },
6855   {
6856     O_GHOST_EXPL_3,                             FALSE,
6857     EL_BD_GHOST,                                ACTION_EXPLODING, -1
6858   },
6859   {
6860     O_GHOST_EXPL_4,                             FALSE,
6861     EL_BD_GHOST,                                ACTION_EXPLODING, -1
6862   },
6863   {
6864     O_BOMB_EXPL_1,                              FALSE,
6865     EL_BD_BOMB,                                 ACTION_EXPLODING, -1
6866   },
6867   {
6868     O_BOMB_EXPL_2,                              FALSE,
6869     EL_BD_BOMB,                                 ACTION_EXPLODING, -1
6870   },
6871   {
6872     O_BOMB_EXPL_3,                              FALSE,
6873     EL_BD_BOMB,                                 ACTION_EXPLODING, -1
6874   },
6875   {
6876     O_BOMB_EXPL_4,                              FALSE,
6877     EL_BD_BOMB,                                 ACTION_EXPLODING, -1
6878   },
6879   {
6880     O_NITRO_EXPL_1,                             FALSE,
6881     EL_BD_NITRO_PACK,                           ACTION_EXPLODING, -1
6882   },
6883   {
6884     O_NITRO_EXPL_2,                             FALSE,
6885     EL_BD_NITRO_PACK,                           ACTION_EXPLODING, -1
6886   },
6887   {
6888     O_NITRO_EXPL_3,                             FALSE,
6889     EL_BD_NITRO_PACK,                           ACTION_EXPLODING, -1
6890   },
6891   {
6892     O_NITRO_EXPL_4,                             FALSE,
6893     EL_BD_NITRO_PACK,                           ACTION_EXPLODING, -1
6894   },
6895   {
6896     O_AMOEBA_2_EXPL_1,                          FALSE,
6897     EL_BD_AMOEBA_2,                             ACTION_EXPLODING, -1
6898   },
6899   {
6900     O_AMOEBA_2_EXPL_2,                          FALSE,
6901     EL_BD_AMOEBA_2,                             ACTION_EXPLODING, -1
6902   },
6903   {
6904     O_AMOEBA_2_EXPL_3,                          FALSE,
6905     EL_BD_AMOEBA_2,                             ACTION_EXPLODING, -1
6906   },
6907   {
6908     O_AMOEBA_2_EXPL_4,                          FALSE,
6909     EL_BD_AMOEBA_2,                             ACTION_EXPLODING, -1
6910   },
6911   {
6912     O_NUT_EXPL_1,                               FALSE,
6913     EL_BD_NUT,                                  ACTION_BREAKING, -1
6914   },
6915   {
6916     O_NUT_EXPL_2,                               FALSE,
6917     EL_BD_NUT,                                  ACTION_BREAKING, -1
6918   },
6919   {
6920     O_NUT_EXPL_3,                               FALSE,
6921     EL_BD_NUT,                                  ACTION_BREAKING, -1
6922   },
6923   {
6924     O_NUT_EXPL_4,                               FALSE,
6925     EL_BD_NUT,                                  ACTION_BREAKING, -1
6926   },
6927   {
6928     O_PLAYER_PNEUMATIC_LEFT,                    FALSE,
6929     EL_BD_PLAYER,                               ACTION_HITTING, MV_BIT_LEFT
6930   },
6931   {
6932     O_PLAYER_PNEUMATIC_RIGHT,                   FALSE,
6933     EL_BD_PLAYER,                               ACTION_HITTING, MV_BIT_RIGHT
6934   },
6935   {
6936     O_PNEUMATIC_ACTIVE_LEFT,                    TRUE,
6937     EL_BD_PNEUMATIC_HAMMER,                     ACTION_HITTING, MV_BIT_LEFT
6938   },
6939   {
6940     O_PNEUMATIC_ACTIVE_RIGHT,                   TRUE,
6941     EL_BD_PNEUMATIC_HAMMER,                     ACTION_HITTING, MV_BIT_RIGHT
6942   },
6943
6944   // helper (runtime) elements
6945
6946   {
6947     O_FAKE_BONUS,                               FALSE,
6948     EL_BD_FAKE_BONUS,                           -1, -1
6949   },
6950   {
6951     O_INBOX_CLOSED,                             FALSE,
6952     EL_BD_INBOX,                                -1, -1
6953   },
6954   {
6955     O_INBOX_OPEN,                               FALSE,
6956     EL_BD_INBOX,                                ACTION_OPENING, -1
6957   },
6958   {
6959     O_OUTBOX_CLOSED,                            FALSE,
6960     EL_BD_EXIT_CLOSED,                          -1, -1
6961   },
6962   {
6963     O_OUTBOX_OPEN,                              FALSE,
6964     EL_BD_EXIT_OPEN,                            -1, -1
6965   },
6966   {
6967     O_COVERED,                                  FALSE,
6968     EL_BD_COVERED,                              -1, -1
6969   },
6970   {
6971     O_PLAYER_LEFT,                              FALSE,
6972     EL_BD_PLAYER,                               ACTION_MOVING, MV_BIT_LEFT
6973   },
6974   {
6975     O_PLAYER_RIGHT,                             FALSE,
6976     EL_BD_PLAYER,                               ACTION_MOVING, MV_BIT_RIGHT
6977   },
6978   {
6979     O_PLAYER_BLINK,                             FALSE,
6980     EL_BD_PLAYER,                               ACTION_BORING_1, -1
6981   },
6982   {
6983     O_PLAYER_TAP,                               FALSE,
6984     EL_BD_PLAYER,                               ACTION_BORING_2, -1
6985   },
6986   {
6987     O_PLAYER_TAP_BLINK,                         FALSE,
6988     EL_BD_PLAYER,                               ACTION_BORING_3, -1
6989   },
6990   {
6991     O_CREATURE_SWITCH_ON,                       FALSE,
6992     EL_BD_CREATURE_SWITCH_ACTIVE,               -1, -1
6993   },
6994   {
6995     O_EXPANDING_WALL_SWITCH_HORIZ,              FALSE,
6996     EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL,    -1, -1
6997   },
6998   {
6999     O_EXPANDING_WALL_SWITCH_VERT,               FALSE,
7000     EL_BD_EXPANDABLE_WALL_SWITCH_VERTICAL,      -1, -1
7001   },
7002   {
7003     O_GRAVITY_SWITCH_ACTIVE,                    FALSE,
7004     EL_BD_GRAVITY_SWITCH_ACTIVE,                -1, -1
7005   },
7006   {
7007     O_REPLICATOR_SWITCH_OFF,                    FALSE,
7008     EL_BD_REPLICATOR_SWITCH,                    -1, -1
7009   },
7010   {
7011     O_REPLICATOR_SWITCH_ON,                     FALSE,
7012     EL_BD_REPLICATOR_SWITCH_ACTIVE,             -1, -1
7013   },
7014   {
7015     O_CONVEYOR_DIR_NORMAL,                      FALSE,
7016     EL_BD_CONVEYOR_DIR_SWITCH_RIGHT,            -1, -1
7017   },
7018   {
7019     O_CONVEYOR_DIR_CHANGED,                     FALSE,
7020     EL_BD_CONVEYOR_DIR_SWITCH_LEFT,             -1, -1
7021   },
7022   {
7023     O_CONVEYOR_SWITCH_OFF,                      FALSE,
7024     EL_BD_CONVEYOR_SWITCH,                      -1, -1
7025   },
7026   {
7027     O_CONVEYOR_SWITCH_ON,                       FALSE,
7028     EL_BD_CONVEYOR_SWITCH_ACTIVE,               -1, -1
7029   },
7030   {
7031     O_MAGIC_WALL_ACTIVE,                        FALSE,
7032     EL_BD_MAGIC_WALL_ACTIVE,                    -1, -1
7033   },
7034   {
7035     O_REPLICATOR_ACTIVE,                        FALSE,
7036     EL_BD_REPLICATOR_ACTIVE,                    -1, -1
7037   },
7038   {
7039     O_CONVEYOR_LEFT_ACTIVE,                     FALSE,
7040     EL_BD_CONVEYOR_LEFT_ACTIVE,                 -1, -1
7041   },
7042   {
7043     O_CONVEYOR_RIGHT_ACTIVE,                    FALSE,
7044     EL_BD_CONVEYOR_RIGHT_ACTIVE,                -1, -1
7045   },
7046   {
7047     O_BITER_SWITCH_1,                           FALSE,
7048     EL_BD_BITER_SWITCH_1,                       -1, -1
7049   },
7050   {
7051     O_BITER_SWITCH_2,                           FALSE,
7052     EL_BD_BITER_SWITCH_2,                       -1, -1
7053   },
7054   {
7055     O_BITER_SWITCH_3,                           FALSE,
7056     EL_BD_BITER_SWITCH_3,                       -1, -1
7057   },
7058   {
7059     O_BITER_SWITCH_4,                           FALSE,
7060     EL_BD_BITER_SWITCH_4,                       -1, -1
7061   },
7062
7063   {
7064     -1,                                         FALSE,
7065     -1,                                         -1, -1
7066   }
7067 };
7068
7069 int map_element_RND_to_BD(int element_rnd)
7070 {
7071   static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS];
7072   static boolean mapping_initialized = FALSE;
7073
7074   if (!mapping_initialized)
7075   {
7076     int i;
7077
7078     // return "O_UNKNOWN" for all undefined elements in mapping array
7079     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7080       mapping_RND_to_BD[i] = O_UNKNOWN;
7081
7082     for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7083       if (bd_object_mapping_list[i].is_rnd_to_bd_mapping)
7084         mapping_RND_to_BD[bd_object_mapping_list[i].element_rnd] =
7085           bd_object_mapping_list[i].element_bd;
7086
7087     mapping_initialized = TRUE;
7088   }
7089
7090   if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7091   {
7092     Warn("invalid RND element %d", element_rnd);
7093
7094     return O_UNKNOWN;
7095   }
7096
7097   return mapping_RND_to_BD[element_rnd];
7098 }
7099
7100 int map_element_BD_to_RND(int element_bd)
7101 {
7102   static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7103   static boolean mapping_initialized = FALSE;
7104
7105   if (!mapping_initialized)
7106   {
7107     int i;
7108
7109     // return "EL_UNKNOWN" for all undefined elements in mapping array
7110     for (i = 0; i < O_MAX_ALL; i++)
7111       mapping_BD_to_RND[i] = EL_UNKNOWN;
7112
7113     for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7114       mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7115         bd_object_mapping_list[i].element_rnd;
7116
7117     mapping_initialized = TRUE;
7118   }
7119
7120   if (element_bd < 0 || element_bd >= O_MAX_ALL)
7121   {
7122     Warn("invalid BD element %d", element_bd);
7123
7124     return EL_UNKNOWN;
7125   }
7126
7127   return mapping_BD_to_RND[element_bd];
7128 }
7129
7130 static struct Mapping_EM_to_RND_object
7131 {
7132   int element_em;
7133   boolean is_rnd_to_em_mapping;         // unique mapping EM <-> RND
7134   boolean is_backside;                  // backside of moving element
7135
7136   int element_rnd;
7137   int action;
7138   int direction;
7139 }
7140 em_object_mapping_list[GAME_TILE_MAX + 1] =
7141 {
7142   {
7143     Zborder,                            FALSE,  FALSE,
7144     EL_EMPTY,                           -1, -1
7145   },
7146   {
7147     Zplayer,                            FALSE,  FALSE,
7148     EL_EMPTY,                           -1, -1
7149   },
7150
7151   {
7152     Zbug,                               FALSE,  FALSE,
7153     EL_EMPTY,                           -1, -1
7154   },
7155   {
7156     Ztank,                              FALSE,  FALSE,
7157     EL_EMPTY,                           -1, -1
7158   },
7159   {
7160     Zeater,                             FALSE,  FALSE,
7161     EL_EMPTY,                           -1, -1
7162   },
7163   {
7164     Zdynamite,                          FALSE,  FALSE,
7165     EL_EMPTY,                           -1, -1
7166   },
7167   {
7168     Zboom,                              FALSE,  FALSE,
7169     EL_EMPTY,                           -1, -1
7170   },
7171
7172   {
7173     Xchain,                             FALSE,  FALSE,
7174     EL_DEFAULT,                         ACTION_EXPLODING, -1
7175   },
7176   {
7177     Xboom_bug,                          FALSE,  FALSE,
7178     EL_BUG,                             ACTION_EXPLODING, -1
7179   },
7180   {
7181     Xboom_tank,                         FALSE,  FALSE,
7182     EL_SPACESHIP,                       ACTION_EXPLODING, -1
7183   },
7184   {
7185     Xboom_android,                      FALSE,  FALSE,
7186     EL_EMC_ANDROID,                     ACTION_OTHER, -1
7187   },
7188   {
7189     Xboom_1,                            FALSE,  FALSE,
7190     EL_DEFAULT,                         ACTION_EXPLODING, -1
7191   },
7192   {
7193     Xboom_2,                            FALSE,  FALSE,
7194     EL_DEFAULT,                         ACTION_EXPLODING, -1
7195   },
7196
7197   {
7198     Xblank,                             TRUE,   FALSE,
7199     EL_EMPTY,                           -1, -1
7200   },
7201
7202   {
7203     Xsplash_e,                          FALSE,  FALSE,
7204     EL_ACID_SPLASH_RIGHT,               -1, -1
7205   },
7206   {
7207     Xsplash_w,                          FALSE,  FALSE,
7208     EL_ACID_SPLASH_LEFT,                -1, -1
7209   },
7210
7211   {
7212     Xplant,                             TRUE,   FALSE,
7213     EL_EMC_PLANT,                       -1, -1
7214   },
7215   {
7216     Yplant,                             FALSE,  FALSE,
7217     EL_EMC_PLANT,                       -1, -1
7218   },
7219
7220   {
7221     Xacid_1,                            TRUE,   FALSE,
7222     EL_ACID,                            -1, -1
7223   },
7224   {
7225     Xacid_2,                            FALSE,  FALSE,
7226     EL_ACID,                            -1, -1
7227   },
7228   {
7229     Xacid_3,                            FALSE,  FALSE,
7230     EL_ACID,                            -1, -1
7231   },
7232   {
7233     Xacid_4,                            FALSE,  FALSE,
7234     EL_ACID,                            -1, -1
7235   },
7236   {
7237     Xacid_5,                            FALSE,  FALSE,
7238     EL_ACID,                            -1, -1
7239   },
7240   {
7241     Xacid_6,                            FALSE,  FALSE,
7242     EL_ACID,                            -1, -1
7243   },
7244   {
7245     Xacid_7,                            FALSE,  FALSE,
7246     EL_ACID,                            -1, -1
7247   },
7248   {
7249     Xacid_8,                            FALSE,  FALSE,
7250     EL_ACID,                            -1, -1
7251   },
7252
7253   {
7254     Xfake_acid_1,                       TRUE,   FALSE,
7255     EL_EMC_FAKE_ACID,                   -1, -1
7256   },
7257   {
7258     Xfake_acid_2,                       FALSE,  FALSE,
7259     EL_EMC_FAKE_ACID,                   -1, -1
7260   },
7261   {
7262     Xfake_acid_3,                       FALSE,  FALSE,
7263     EL_EMC_FAKE_ACID,                   -1, -1
7264   },
7265   {
7266     Xfake_acid_4,                       FALSE,  FALSE,
7267     EL_EMC_FAKE_ACID,                   -1, -1
7268   },
7269   {
7270     Xfake_acid_5,                       FALSE,  FALSE,
7271     EL_EMC_FAKE_ACID,                   -1, -1
7272   },
7273   {
7274     Xfake_acid_6,                       FALSE,  FALSE,
7275     EL_EMC_FAKE_ACID,                   -1, -1
7276   },
7277   {
7278     Xfake_acid_7,                       FALSE,  FALSE,
7279     EL_EMC_FAKE_ACID,                   -1, -1
7280   },
7281   {
7282     Xfake_acid_8,                       FALSE,  FALSE,
7283     EL_EMC_FAKE_ACID,                   -1, -1
7284   },
7285
7286   {
7287     Xfake_acid_1_player,                FALSE,  FALSE,
7288     EL_EMC_FAKE_ACID,                   -1, -1
7289   },
7290   {
7291     Xfake_acid_2_player,                FALSE,  FALSE,
7292     EL_EMC_FAKE_ACID,                   -1, -1
7293   },
7294   {
7295     Xfake_acid_3_player,                FALSE,  FALSE,
7296     EL_EMC_FAKE_ACID,                   -1, -1
7297   },
7298   {
7299     Xfake_acid_4_player,                FALSE,  FALSE,
7300     EL_EMC_FAKE_ACID,                   -1, -1
7301   },
7302   {
7303     Xfake_acid_5_player,                FALSE,  FALSE,
7304     EL_EMC_FAKE_ACID,                   -1, -1
7305   },
7306   {
7307     Xfake_acid_6_player,                FALSE,  FALSE,
7308     EL_EMC_FAKE_ACID,                   -1, -1
7309   },
7310   {
7311     Xfake_acid_7_player,                FALSE,  FALSE,
7312     EL_EMC_FAKE_ACID,                   -1, -1
7313   },
7314   {
7315     Xfake_acid_8_player,                FALSE,  FALSE,
7316     EL_EMC_FAKE_ACID,                   -1, -1
7317   },
7318
7319   {
7320     Xgrass,                             TRUE,   FALSE,
7321     EL_EMC_GRASS,                       -1, -1
7322   },
7323   {
7324     Ygrass_nB,                          FALSE,  FALSE,
7325     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
7326   },
7327   {
7328     Ygrass_eB,                          FALSE,  FALSE,
7329     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
7330   },
7331   {
7332     Ygrass_sB,                          FALSE,  FALSE,
7333     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
7334   },
7335   {
7336     Ygrass_wB,                          FALSE,  FALSE,
7337     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
7338   },
7339
7340   {
7341     Xdirt,                              TRUE,   FALSE,
7342     EL_SAND,                            -1, -1
7343   },
7344   {
7345     Ydirt_nB,                           FALSE,  FALSE,
7346     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
7347   },
7348   {
7349     Ydirt_eB,                           FALSE,  FALSE,
7350     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
7351   },
7352   {
7353     Ydirt_sB,                           FALSE,  FALSE,
7354     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
7355   },
7356   {
7357     Ydirt_wB,                           FALSE,  FALSE,
7358     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
7359   },
7360
7361   {
7362     Xandroid,                           TRUE,   FALSE,
7363     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
7364   },
7365   {
7366     Xandroid_1_n,                       FALSE,  FALSE,
7367     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
7368   },
7369   {
7370     Xandroid_2_n,                       FALSE,  FALSE,
7371     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
7372   },
7373   {
7374     Xandroid_1_e,                       FALSE,  FALSE,
7375     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
7376   },
7377   {
7378     Xandroid_2_e,                       FALSE,  FALSE,
7379     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
7380   },
7381   {
7382     Xandroid_1_w,                       FALSE,  FALSE,
7383     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
7384   },
7385   {
7386     Xandroid_2_w,                       FALSE,  FALSE,
7387     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
7388   },
7389   {
7390     Xandroid_1_s,                       FALSE,  FALSE,
7391     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
7392   },
7393   {
7394     Xandroid_2_s,                       FALSE,  FALSE,
7395     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
7396   },
7397   {
7398     Yandroid_n,                         FALSE,  FALSE,
7399     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
7400   },
7401   {
7402     Yandroid_nB,                        FALSE,  TRUE,
7403     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
7404   },
7405   {
7406     Yandroid_ne,                        FALSE,  FALSE,
7407     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
7408   },
7409   {
7410     Yandroid_neB,                       FALSE,  TRUE,
7411     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
7412   },
7413   {
7414     Yandroid_e,                         FALSE,  FALSE,
7415     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
7416   },
7417   {
7418     Yandroid_eB,                        FALSE,  TRUE,
7419     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
7420   },
7421   {
7422     Yandroid_se,                        FALSE,  FALSE,
7423     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
7424   },
7425   {
7426     Yandroid_seB,                       FALSE,  TRUE,
7427     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
7428   },
7429   {
7430     Yandroid_s,                         FALSE,  FALSE,
7431     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
7432   },
7433   {
7434     Yandroid_sB,                        FALSE,  TRUE,
7435     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
7436   },
7437   {
7438     Yandroid_sw,                        FALSE,  FALSE,
7439     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
7440   },
7441   {
7442     Yandroid_swB,                       FALSE,  TRUE,
7443     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
7444   },
7445   {
7446     Yandroid_w,                         FALSE,  FALSE,
7447     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
7448   },
7449   {
7450     Yandroid_wB,                        FALSE,  TRUE,
7451     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
7452   },
7453   {
7454     Yandroid_nw,                        FALSE,  FALSE,
7455     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
7456   },
7457   {
7458     Yandroid_nwB,                       FALSE,  TRUE,
7459     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
7460   },
7461
7462   {
7463     Xeater_n,                           TRUE,   FALSE,
7464     EL_YAMYAM_UP,                       -1, -1
7465   },
7466   {
7467     Xeater_e,                           TRUE,   FALSE,
7468     EL_YAMYAM_RIGHT,                    -1, -1
7469   },
7470   {
7471     Xeater_w,                           TRUE,   FALSE,
7472     EL_YAMYAM_LEFT,                     -1, -1
7473   },
7474   {
7475     Xeater_s,                           TRUE,   FALSE,
7476     EL_YAMYAM_DOWN,                     -1, -1
7477   },
7478   {
7479     Yeater_n,                           FALSE,  FALSE,
7480     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
7481   },
7482   {
7483     Yeater_nB,                          FALSE,  TRUE,
7484     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
7485   },
7486   {
7487     Yeater_e,                           FALSE,  FALSE,
7488     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
7489   },
7490   {
7491     Yeater_eB,                          FALSE,  TRUE,
7492     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
7493   },
7494   {
7495     Yeater_s,                           FALSE,  FALSE,
7496     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
7497   },
7498   {
7499     Yeater_sB,                          FALSE,  TRUE,
7500     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
7501   },
7502   {
7503     Yeater_w,                           FALSE,  FALSE,
7504     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
7505   },
7506   {
7507     Yeater_wB,                          FALSE,  TRUE,
7508     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
7509   },
7510   {
7511     Yeater_stone,                       FALSE,  FALSE,
7512     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
7513   },
7514   {
7515     Yeater_spring,                      FALSE,  FALSE,
7516     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
7517   },
7518
7519   {
7520     Xalien,                             TRUE,   FALSE,
7521     EL_ROBOT,                           -1, -1
7522   },
7523   {
7524     Xalien_pause,                       FALSE,  FALSE,
7525     EL_ROBOT,                           -1, -1
7526   },
7527   {
7528     Yalien_n,                           FALSE,  FALSE,
7529     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
7530   },
7531   {
7532     Yalien_nB,                          FALSE,  TRUE,
7533     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
7534   },
7535   {
7536     Yalien_e,                           FALSE,  FALSE,
7537     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
7538   },
7539   {
7540     Yalien_eB,                          FALSE,  TRUE,
7541     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
7542   },
7543   {
7544     Yalien_s,                           FALSE,  FALSE,
7545     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
7546   },
7547   {
7548     Yalien_sB,                          FALSE,  TRUE,
7549     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
7550   },
7551   {
7552     Yalien_w,                           FALSE,  FALSE,
7553     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
7554   },
7555   {
7556     Yalien_wB,                          FALSE,  TRUE,
7557     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
7558   },
7559   {
7560     Yalien_stone,                       FALSE,  FALSE,
7561     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
7562   },
7563   {
7564     Yalien_spring,                      FALSE,  FALSE,
7565     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
7566   },
7567
7568   {
7569     Xbug_1_n,                           TRUE,   FALSE,
7570     EL_BUG_UP,                          -1, -1
7571   },
7572   {
7573     Xbug_1_e,                           TRUE,   FALSE,
7574     EL_BUG_RIGHT,                       -1, -1
7575   },
7576   {
7577     Xbug_1_s,                           TRUE,   FALSE,
7578     EL_BUG_DOWN,                        -1, -1
7579   },
7580   {
7581     Xbug_1_w,                           TRUE,   FALSE,
7582     EL_BUG_LEFT,                        -1, -1
7583   },
7584   {
7585     Xbug_2_n,                           FALSE,  FALSE,
7586     EL_BUG_UP,                          -1, -1
7587   },
7588   {
7589     Xbug_2_e,                           FALSE,  FALSE,
7590     EL_BUG_RIGHT,                       -1, -1
7591   },
7592   {
7593     Xbug_2_s,                           FALSE,  FALSE,
7594     EL_BUG_DOWN,                        -1, -1
7595   },
7596   {
7597     Xbug_2_w,                           FALSE,  FALSE,
7598     EL_BUG_LEFT,                        -1, -1
7599   },
7600   {
7601     Ybug_n,                             FALSE,  FALSE,
7602     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
7603   },
7604   {
7605     Ybug_nB,                            FALSE,  TRUE,
7606     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
7607   },
7608   {
7609     Ybug_e,                             FALSE,  FALSE,
7610     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
7611   },
7612   {
7613     Ybug_eB,                            FALSE,  TRUE,
7614     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
7615   },
7616   {
7617     Ybug_s,                             FALSE,  FALSE,
7618     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
7619   },
7620   {
7621     Ybug_sB,                            FALSE,  TRUE,
7622     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
7623   },
7624   {
7625     Ybug_w,                             FALSE,  FALSE,
7626     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
7627   },
7628   {
7629     Ybug_wB,                            FALSE,  TRUE,
7630     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
7631   },
7632   {
7633     Ybug_w_n,                           FALSE,  FALSE,
7634     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
7635   },
7636   {
7637     Ybug_n_e,                           FALSE,  FALSE,
7638     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
7639   },
7640   {
7641     Ybug_e_s,                           FALSE,  FALSE,
7642     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
7643   },
7644   {
7645     Ybug_s_w,                           FALSE,  FALSE,
7646     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
7647   },
7648   {
7649     Ybug_e_n,                           FALSE,  FALSE,
7650     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
7651   },
7652   {
7653     Ybug_s_e,                           FALSE,  FALSE,
7654     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
7655   },
7656   {
7657     Ybug_w_s,                           FALSE,  FALSE,
7658     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
7659   },
7660   {
7661     Ybug_n_w,                           FALSE,  FALSE,
7662     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
7663   },
7664   {
7665     Ybug_stone,                         FALSE,  FALSE,
7666     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
7667   },
7668   {
7669     Ybug_spring,                        FALSE,  FALSE,
7670     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
7671   },
7672
7673   {
7674     Xtank_1_n,                          TRUE,   FALSE,
7675     EL_SPACESHIP_UP,                    -1, -1
7676   },
7677   {
7678     Xtank_1_e,                          TRUE,   FALSE,
7679     EL_SPACESHIP_RIGHT,                 -1, -1
7680   },
7681   {
7682     Xtank_1_s,                          TRUE,   FALSE,
7683     EL_SPACESHIP_DOWN,                  -1, -1
7684   },
7685   {
7686     Xtank_1_w,                          TRUE,   FALSE,
7687     EL_SPACESHIP_LEFT,                  -1, -1
7688   },
7689   {
7690     Xtank_2_n,                          FALSE,  FALSE,
7691     EL_SPACESHIP_UP,                    -1, -1
7692   },
7693   {
7694     Xtank_2_e,                          FALSE,  FALSE,
7695     EL_SPACESHIP_RIGHT,                 -1, -1
7696   },
7697   {
7698     Xtank_2_s,                          FALSE,  FALSE,
7699     EL_SPACESHIP_DOWN,                  -1, -1
7700   },
7701   {
7702     Xtank_2_w,                          FALSE,  FALSE,
7703     EL_SPACESHIP_LEFT,                  -1, -1
7704   },
7705   {
7706     Ytank_n,                            FALSE,  FALSE,
7707     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
7708   },
7709   {
7710     Ytank_nB,                           FALSE,  TRUE,
7711     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
7712   },
7713   {
7714     Ytank_e,                            FALSE,  FALSE,
7715     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
7716   },
7717   {
7718     Ytank_eB,                           FALSE,  TRUE,
7719     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
7720   },
7721   {
7722     Ytank_s,                            FALSE,  FALSE,
7723     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
7724   },
7725   {
7726     Ytank_sB,                           FALSE,  TRUE,
7727     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
7728   },
7729   {
7730     Ytank_w,                            FALSE,  FALSE,
7731     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
7732   },
7733   {
7734     Ytank_wB,                           FALSE,  TRUE,
7735     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
7736   },
7737   {
7738     Ytank_w_n,                          FALSE,  FALSE,
7739     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
7740   },
7741   {
7742     Ytank_n_e,                          FALSE,  FALSE,
7743     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
7744   },
7745   {
7746     Ytank_e_s,                          FALSE,  FALSE,
7747     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
7748   },
7749   {
7750     Ytank_s_w,                          FALSE,  FALSE,
7751     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
7752   },
7753   {
7754     Ytank_e_n,                          FALSE,  FALSE,
7755     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
7756   },
7757   {
7758     Ytank_s_e,                          FALSE,  FALSE,
7759     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
7760   },
7761   {
7762     Ytank_w_s,                          FALSE,  FALSE,
7763     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
7764   },
7765   {
7766     Ytank_n_w,                          FALSE,  FALSE,
7767     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
7768   },
7769   {
7770     Ytank_stone,                        FALSE,  FALSE,
7771     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
7772   },
7773   {
7774     Ytank_spring,                       FALSE,  FALSE,
7775     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
7776   },
7777
7778   {
7779     Xemerald,                           TRUE,   FALSE,
7780     EL_EMERALD,                         -1, -1
7781   },
7782   {
7783     Xemerald_pause,                     FALSE,  FALSE,
7784     EL_EMERALD,                         -1, -1
7785   },
7786   {
7787     Xemerald_fall,                      FALSE,  FALSE,
7788     EL_EMERALD,                         -1, -1
7789   },
7790   {
7791     Xemerald_shine,                     FALSE,  FALSE,
7792     EL_EMERALD,                         ACTION_TWINKLING, -1
7793   },
7794   {
7795     Yemerald_s,                         FALSE,  FALSE,
7796     EL_EMERALD,                         ACTION_FALLING, -1
7797   },
7798   {
7799     Yemerald_sB,                        FALSE,  TRUE,
7800     EL_EMERALD,                         ACTION_FALLING, -1
7801   },
7802   {
7803     Yemerald_e,                         FALSE,  FALSE,
7804     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
7805   },
7806   {
7807     Yemerald_eB,                        FALSE,  TRUE,
7808     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
7809   },
7810   {
7811     Yemerald_w,                         FALSE,  FALSE,
7812     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
7813   },
7814   {
7815     Yemerald_wB,                        FALSE,  TRUE,
7816     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
7817   },
7818   {
7819     Yemerald_blank,                     FALSE,  FALSE,
7820     EL_EMERALD,                         ACTION_COLLECTING, -1
7821   },
7822
7823   {
7824     Xdiamond,                           TRUE,   FALSE,
7825     EL_DIAMOND,                         -1, -1
7826   },
7827   {
7828     Xdiamond_pause,                     FALSE,  FALSE,
7829     EL_DIAMOND,                         -1, -1
7830   },
7831   {
7832     Xdiamond_fall,                      FALSE,  FALSE,
7833     EL_DIAMOND,                         -1, -1
7834   },
7835   {
7836     Xdiamond_shine,                     FALSE,  FALSE,
7837     EL_DIAMOND,                         ACTION_TWINKLING, -1
7838   },
7839   {
7840     Ydiamond_s,                         FALSE,  FALSE,
7841     EL_DIAMOND,                         ACTION_FALLING, -1
7842   },
7843   {
7844     Ydiamond_sB,                        FALSE,  TRUE,
7845     EL_DIAMOND,                         ACTION_FALLING, -1
7846   },
7847   {
7848     Ydiamond_e,                         FALSE,  FALSE,
7849     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
7850   },
7851   {
7852     Ydiamond_eB,                        FALSE,  TRUE,
7853     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
7854   },
7855   {
7856     Ydiamond_w,                         FALSE,  FALSE,
7857     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
7858   },
7859   {
7860     Ydiamond_wB,                        FALSE,  TRUE,
7861     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
7862   },
7863   {
7864     Ydiamond_blank,                     FALSE,  FALSE,
7865     EL_DIAMOND,                         ACTION_COLLECTING, -1
7866   },
7867   {
7868     Ydiamond_stone,                     FALSE,  FALSE,
7869     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
7870   },
7871
7872   {
7873     Xstone,                             TRUE,   FALSE,
7874     EL_ROCK,                            -1, -1
7875   },
7876   {
7877     Xstone_pause,                       FALSE,  FALSE,
7878     EL_ROCK,                            -1, -1
7879   },
7880   {
7881     Xstone_fall,                        FALSE,  FALSE,
7882     EL_ROCK,                            -1, -1
7883   },
7884   {
7885     Ystone_s,                           FALSE,  FALSE,
7886     EL_ROCK,                            ACTION_FALLING, -1
7887   },
7888   {
7889     Ystone_sB,                          FALSE,  TRUE,
7890     EL_ROCK,                            ACTION_FALLING, -1
7891   },
7892   {
7893     Ystone_e,                           FALSE,  FALSE,
7894     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
7895   },
7896   {
7897     Ystone_eB,                          FALSE,  TRUE,
7898     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
7899   },
7900   {
7901     Ystone_w,                           FALSE,  FALSE,
7902     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
7903   },
7904   {
7905     Ystone_wB,                          FALSE,  TRUE,
7906     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
7907   },
7908
7909   {
7910     Xbomb,                              TRUE,   FALSE,
7911     EL_BOMB,                            -1, -1
7912   },
7913   {
7914     Xbomb_pause,                        FALSE,  FALSE,
7915     EL_BOMB,                            -1, -1
7916   },
7917   {
7918     Xbomb_fall,                         FALSE,  FALSE,
7919     EL_BOMB,                            -1, -1
7920   },
7921   {
7922     Ybomb_s,                            FALSE,  FALSE,
7923     EL_BOMB,                            ACTION_FALLING, -1
7924   },
7925   {
7926     Ybomb_sB,                           FALSE,  TRUE,
7927     EL_BOMB,                            ACTION_FALLING, -1
7928   },
7929   {
7930     Ybomb_e,                            FALSE,  FALSE,
7931     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
7932   },
7933   {
7934     Ybomb_eB,                           FALSE,  TRUE,
7935     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
7936   },
7937   {
7938     Ybomb_w,                            FALSE,  FALSE,
7939     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
7940   },
7941   {
7942     Ybomb_wB,                           FALSE,  TRUE,
7943     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
7944   },
7945   {
7946     Ybomb_blank,                        FALSE,  FALSE,
7947     EL_BOMB,                            ACTION_ACTIVATING, -1
7948   },
7949
7950   {
7951     Xnut,                               TRUE,   FALSE,
7952     EL_NUT,                             -1, -1
7953   },
7954   {
7955     Xnut_pause,                         FALSE,  FALSE,
7956     EL_NUT,                             -1, -1
7957   },
7958   {
7959     Xnut_fall,                          FALSE,  FALSE,
7960     EL_NUT,                             -1, -1
7961   },
7962   {
7963     Ynut_s,                             FALSE,  FALSE,
7964     EL_NUT,                             ACTION_FALLING, -1
7965   },
7966   {
7967     Ynut_sB,                            FALSE,  TRUE,
7968     EL_NUT,                             ACTION_FALLING, -1
7969   },
7970   {
7971     Ynut_e,                             FALSE,  FALSE,
7972     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
7973   },
7974   {
7975     Ynut_eB,                            FALSE,  TRUE,
7976     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
7977   },
7978   {
7979     Ynut_w,                             FALSE,  FALSE,
7980     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
7981   },
7982   {
7983     Ynut_wB,                            FALSE,  TRUE,
7984     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
7985   },
7986   {
7987     Ynut_stone,                         FALSE,  FALSE,
7988     EL_NUT,                             ACTION_BREAKING, -1
7989   },
7990
7991   {
7992     Xspring,                            TRUE,   FALSE,
7993     EL_SPRING,                          -1, -1
7994   },
7995   {
7996     Xspring_pause,                      FALSE,  FALSE,
7997     EL_SPRING,                          -1, -1
7998   },
7999   {
8000     Xspring_e,                          TRUE,   FALSE,
8001     EL_SPRING_RIGHT,                    -1, -1
8002   },
8003   {
8004     Xspring_w,                          TRUE,   FALSE,
8005     EL_SPRING_LEFT,                     -1, -1
8006   },
8007   {
8008     Xspring_fall,                       FALSE,  FALSE,
8009     EL_SPRING,                          -1, -1
8010   },
8011   {
8012     Yspring_s,                          FALSE,  FALSE,
8013     EL_SPRING,                          ACTION_FALLING, -1
8014   },
8015   {
8016     Yspring_sB,                         FALSE,  TRUE,
8017     EL_SPRING,                          ACTION_FALLING, -1
8018   },
8019   {
8020     Yspring_e,                          FALSE,  FALSE,
8021     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
8022   },
8023   {
8024     Yspring_eB,                         FALSE,  TRUE,
8025     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
8026   },
8027   {
8028     Yspring_w,                          FALSE,  FALSE,
8029     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
8030   },
8031   {
8032     Yspring_wB,                         FALSE,  TRUE,
8033     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
8034   },
8035   {
8036     Yspring_alien_e,                    FALSE,  FALSE,
8037     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
8038   },
8039   {
8040     Yspring_alien_eB,                   FALSE,  TRUE,
8041     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
8042   },
8043   {
8044     Yspring_alien_w,                    FALSE,  FALSE,
8045     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
8046   },
8047   {
8048     Yspring_alien_wB,                   FALSE,  TRUE,
8049     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
8050   },
8051
8052   {
8053     Xpush_emerald_e,                    FALSE,  FALSE,
8054     EL_EMERALD,                         -1, MV_BIT_RIGHT
8055   },
8056   {
8057     Xpush_emerald_w,                    FALSE,  FALSE,
8058     EL_EMERALD,                         -1, MV_BIT_LEFT
8059   },
8060   {
8061     Xpush_diamond_e,                    FALSE,  FALSE,
8062     EL_DIAMOND,                         -1, MV_BIT_RIGHT
8063   },
8064   {
8065     Xpush_diamond_w,                    FALSE,  FALSE,
8066     EL_DIAMOND,                         -1, MV_BIT_LEFT
8067   },
8068   {
8069     Xpush_stone_e,                      FALSE,  FALSE,
8070     EL_ROCK,                            -1, MV_BIT_RIGHT
8071   },
8072   {
8073     Xpush_stone_w,                      FALSE,  FALSE,
8074     EL_ROCK,                            -1, MV_BIT_LEFT
8075   },
8076   {
8077     Xpush_bomb_e,                       FALSE,  FALSE,
8078     EL_BOMB,                            -1, MV_BIT_RIGHT
8079   },
8080   {
8081     Xpush_bomb_w,                       FALSE,  FALSE,
8082     EL_BOMB,                            -1, MV_BIT_LEFT
8083   },
8084   {
8085     Xpush_nut_e,                        FALSE,  FALSE,
8086     EL_NUT,                             -1, MV_BIT_RIGHT
8087   },
8088   {
8089     Xpush_nut_w,                        FALSE,  FALSE,
8090     EL_NUT,                             -1, MV_BIT_LEFT
8091   },
8092   {
8093     Xpush_spring_e,                     FALSE,  FALSE,
8094     EL_SPRING_RIGHT,                    -1, MV_BIT_RIGHT
8095   },
8096   {
8097     Xpush_spring_w,                     FALSE,  FALSE,
8098     EL_SPRING_LEFT,                     -1, MV_BIT_LEFT
8099   },
8100
8101   {
8102     Xdynamite,                          TRUE,   FALSE,
8103     EL_EM_DYNAMITE,                     -1, -1
8104   },
8105   {
8106     Ydynamite_blank,                    FALSE,  FALSE,
8107     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
8108   },
8109   {
8110     Xdynamite_1,                        TRUE,   FALSE,
8111     EL_EM_DYNAMITE_ACTIVE,              -1, -1
8112   },
8113   {
8114     Xdynamite_2,                        FALSE,  FALSE,
8115     EL_EM_DYNAMITE_ACTIVE,              -1, -1
8116   },
8117   {
8118     Xdynamite_3,                        FALSE,  FALSE,
8119     EL_EM_DYNAMITE_ACTIVE,              -1, -1
8120   },
8121   {
8122     Xdynamite_4,                        FALSE,  FALSE,
8123     EL_EM_DYNAMITE_ACTIVE,              -1, -1
8124   },
8125
8126   {
8127     Xkey_1,                             TRUE,   FALSE,
8128     EL_EM_KEY_1,                        -1, -1
8129   },
8130   {
8131     Xkey_2,                             TRUE,   FALSE,
8132     EL_EM_KEY_2,                        -1, -1
8133   },
8134   {
8135     Xkey_3,                             TRUE,   FALSE,
8136     EL_EM_KEY_3,                        -1, -1
8137   },
8138   {
8139     Xkey_4,                             TRUE,   FALSE,
8140     EL_EM_KEY_4,                        -1, -1
8141   },
8142   {
8143     Xkey_5,                             TRUE,   FALSE,
8144     EL_EMC_KEY_5,                       -1, -1
8145   },
8146   {
8147     Xkey_6,                             TRUE,   FALSE,
8148     EL_EMC_KEY_6,                       -1, -1
8149   },
8150   {
8151     Xkey_7,                             TRUE,   FALSE,
8152     EL_EMC_KEY_7,                       -1, -1
8153   },
8154   {
8155     Xkey_8,                             TRUE,   FALSE,
8156     EL_EMC_KEY_8,                       -1, -1
8157   },
8158
8159   {
8160     Xdoor_1,                            TRUE,   FALSE,
8161     EL_EM_GATE_1,                       -1, -1
8162   },
8163   {
8164     Xdoor_2,                            TRUE,   FALSE,
8165     EL_EM_GATE_2,                       -1, -1
8166   },
8167   {
8168     Xdoor_3,                            TRUE,   FALSE,
8169     EL_EM_GATE_3,                       -1, -1
8170   },
8171   {
8172     Xdoor_4,                            TRUE,   FALSE,
8173     EL_EM_GATE_4,                       -1, -1
8174   },
8175   {
8176     Xdoor_5,                            TRUE,   FALSE,
8177     EL_EMC_GATE_5,                      -1, -1
8178   },
8179   {
8180     Xdoor_6,                            TRUE,   FALSE,
8181     EL_EMC_GATE_6,                      -1, -1
8182   },
8183   {
8184     Xdoor_7,                            TRUE,   FALSE,
8185     EL_EMC_GATE_7,                      -1, -1
8186   },
8187   {
8188     Xdoor_8,                            TRUE,   FALSE,
8189     EL_EMC_GATE_8,                      -1, -1
8190   },
8191
8192   {
8193     Xfake_door_1,                       TRUE,   FALSE,
8194     EL_EM_GATE_1_GRAY,                  -1, -1
8195   },
8196   {
8197     Xfake_door_2,                       TRUE,   FALSE,
8198     EL_EM_GATE_2_GRAY,                  -1, -1
8199   },
8200   {
8201     Xfake_door_3,                       TRUE,   FALSE,
8202     EL_EM_GATE_3_GRAY,                  -1, -1
8203   },
8204   {
8205     Xfake_door_4,                       TRUE,   FALSE,
8206     EL_EM_GATE_4_GRAY,                  -1, -1
8207   },
8208   {
8209     Xfake_door_5,                       TRUE,   FALSE,
8210     EL_EMC_GATE_5_GRAY,                 -1, -1
8211   },
8212   {
8213     Xfake_door_6,                       TRUE,   FALSE,
8214     EL_EMC_GATE_6_GRAY,                 -1, -1
8215   },
8216   {
8217     Xfake_door_7,                       TRUE,   FALSE,
8218     EL_EMC_GATE_7_GRAY,                 -1, -1
8219   },
8220   {
8221     Xfake_door_8,                       TRUE,   FALSE,
8222     EL_EMC_GATE_8_GRAY,                 -1, -1
8223   },
8224
8225   {
8226     Xballoon,                           TRUE,   FALSE,
8227     EL_BALLOON,                         -1, -1
8228   },
8229   {
8230     Yballoon_n,                         FALSE,  FALSE,
8231     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
8232   },
8233   {
8234     Yballoon_nB,                        FALSE,  TRUE,
8235     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
8236   },
8237   {
8238     Yballoon_e,                         FALSE,  FALSE,
8239     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
8240   },
8241   {
8242     Yballoon_eB,                        FALSE,  TRUE,
8243     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
8244   },
8245   {
8246     Yballoon_s,                         FALSE,  FALSE,
8247     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
8248   },
8249   {
8250     Yballoon_sB,                        FALSE,  TRUE,
8251     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
8252   },
8253   {
8254     Yballoon_w,                         FALSE,  FALSE,
8255     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
8256   },
8257   {
8258     Yballoon_wB,                        FALSE,  TRUE,
8259     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
8260   },
8261
8262   {
8263     Xball_1,                            TRUE,   FALSE,
8264     EL_EMC_MAGIC_BALL,                  -1, -1
8265   },
8266   {
8267     Yball_1,                            FALSE,  FALSE,
8268     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
8269   },
8270   {
8271     Xball_2,                            FALSE,  FALSE,
8272     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
8273   },
8274   {
8275     Yball_2,                            FALSE,  FALSE,
8276     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
8277   },
8278   {
8279     Yball_blank,                        FALSE,  FALSE,
8280     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
8281   },
8282
8283   {
8284     Xamoeba_1,                          TRUE,   FALSE,
8285     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
8286   },
8287   {
8288     Xamoeba_2,                          FALSE,  FALSE,
8289     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
8290   },
8291   {
8292     Xamoeba_3,                          FALSE,  FALSE,
8293     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
8294   },
8295   {
8296     Xamoeba_4,                          FALSE,  FALSE,
8297     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
8298   },
8299   {
8300     Xamoeba_5,                          TRUE,   FALSE,
8301     EL_AMOEBA_WET,                      ACTION_OTHER, -1
8302   },
8303   {
8304     Xamoeba_6,                          FALSE,  FALSE,
8305     EL_AMOEBA_WET,                      ACTION_OTHER, -1
8306   },
8307   {
8308     Xamoeba_7,                          FALSE,  FALSE,
8309     EL_AMOEBA_WET,                      ACTION_OTHER, -1
8310   },
8311   {
8312     Xamoeba_8,                          FALSE,  FALSE,
8313     EL_AMOEBA_WET,                      ACTION_OTHER, -1
8314   },
8315
8316   {
8317     Xdrip,                              TRUE,   FALSE,
8318     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
8319   },
8320   {
8321     Xdrip_fall,                         FALSE,  FALSE,
8322     EL_AMOEBA_DROP,                     -1, -1
8323   },
8324   {
8325     Xdrip_stretch,                      FALSE,  FALSE,
8326     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8327   },
8328   {
8329     Xdrip_stretchB,                     FALSE,  TRUE,
8330     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8331   },
8332   {
8333     Ydrip_1_s,                          FALSE,  FALSE,
8334     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8335   },
8336   {
8337     Ydrip_1_sB,                         FALSE,  TRUE,
8338     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8339   },
8340   {
8341     Ydrip_2_s,                          FALSE,  FALSE,
8342     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8343   },
8344   {
8345     Ydrip_2_sB,                         FALSE,  TRUE,
8346     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
8347   },
8348
8349   {
8350     Xwonderwall,                        TRUE,   FALSE,
8351     EL_MAGIC_WALL,                      -1, -1
8352   },
8353   {
8354     Ywonderwall,                        FALSE,  FALSE,
8355     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
8356   },
8357
8358   {
8359     Xwheel,                             TRUE,   FALSE,
8360     EL_ROBOT_WHEEL,                     -1, -1
8361   },
8362   {
8363     Ywheel,                             FALSE,  FALSE,
8364     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
8365   },
8366
8367   {
8368     Xswitch,                            TRUE,   FALSE,
8369     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
8370   },
8371   {
8372     Yswitch,                            FALSE,  FALSE,
8373     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
8374   },
8375
8376   {
8377     Xbumper,                            TRUE,   FALSE,
8378     EL_EMC_SPRING_BUMPER,               -1, -1
8379   },
8380   {
8381     Ybumper,                            FALSE,  FALSE,
8382     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
8383   },
8384
8385   {
8386     Xacid_nw,                           TRUE,   FALSE,
8387     EL_ACID_POOL_TOPLEFT,               -1, -1
8388   },
8389   {
8390     Xacid_ne,                           TRUE,   FALSE,
8391     EL_ACID_POOL_TOPRIGHT,              -1, -1
8392   },
8393   {
8394     Xacid_sw,                           TRUE,   FALSE,
8395     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
8396   },
8397   {
8398     Xacid_s,                            TRUE,   FALSE,
8399     EL_ACID_POOL_BOTTOM,                -1, -1
8400   },
8401   {
8402     Xacid_se,                           TRUE,   FALSE,
8403     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
8404   },
8405
8406   {
8407     Xfake_blank,                        TRUE,   FALSE,
8408     EL_INVISIBLE_WALL,                  -1, -1
8409   },
8410   {
8411     Yfake_blank,                        FALSE,  FALSE,
8412     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
8413   },
8414
8415   {
8416     Xfake_grass,                        TRUE,   FALSE,
8417     EL_EMC_FAKE_GRASS,                  -1, -1
8418   },
8419   {
8420     Yfake_grass,                        FALSE,  FALSE,
8421     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
8422   },
8423
8424   {
8425     Xfake_amoeba,                       TRUE,   FALSE,
8426     EL_EMC_DRIPPER,                     -1, -1
8427   },
8428   {
8429     Yfake_amoeba,                       FALSE,  FALSE,
8430     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
8431   },
8432
8433   {
8434     Xlenses,                            TRUE,   FALSE,
8435     EL_EMC_LENSES,                      -1, -1
8436   },
8437
8438   {
8439     Xmagnify,                           TRUE,   FALSE,
8440     EL_EMC_MAGNIFIER,                   -1, -1
8441   },
8442
8443   {
8444     Xsand,                              TRUE,   FALSE,
8445     EL_QUICKSAND_EMPTY,                 -1, -1
8446   },
8447   {
8448     Xsand_stone,                        TRUE,   FALSE,
8449     EL_QUICKSAND_FULL,                  -1, -1
8450   },
8451   {
8452     Xsand_stonein_1,                    FALSE,  TRUE,
8453     EL_ROCK,                            ACTION_FILLING, -1
8454   },
8455   {
8456     Xsand_stonein_2,                    FALSE,  TRUE,
8457     EL_ROCK,                            ACTION_FILLING, -1
8458   },
8459   {
8460     Xsand_stonein_3,                    FALSE,  TRUE,
8461     EL_ROCK,                            ACTION_FILLING, -1
8462   },
8463   {
8464     Xsand_stonein_4,                    FALSE,  TRUE,
8465     EL_ROCK,                            ACTION_FILLING, -1
8466   },
8467   {
8468     Xsand_sandstone_1,                  FALSE,  FALSE,
8469     EL_QUICKSAND_FILLING,               -1, -1
8470   },
8471   {
8472     Xsand_sandstone_2,                  FALSE,  FALSE,
8473     EL_QUICKSAND_FILLING,               -1, -1
8474   },
8475   {
8476     Xsand_sandstone_3,                  FALSE,  FALSE,
8477     EL_QUICKSAND_FILLING,               -1, -1
8478   },
8479   {
8480     Xsand_sandstone_4,                  FALSE,  FALSE,
8481     EL_QUICKSAND_FILLING,               -1, -1
8482   },
8483   {
8484     Xsand_stonesand_1,                  FALSE,  FALSE,
8485     EL_QUICKSAND_EMPTYING,              -1, -1
8486   },
8487   {
8488     Xsand_stonesand_2,                  FALSE,  FALSE,
8489     EL_QUICKSAND_EMPTYING,              -1, -1
8490   },
8491   {
8492     Xsand_stonesand_3,                  FALSE,  FALSE,
8493     EL_QUICKSAND_EMPTYING,              -1, -1
8494   },
8495   {
8496     Xsand_stonesand_4,                  FALSE,  FALSE,
8497     EL_QUICKSAND_EMPTYING,              -1, -1
8498   },
8499   {
8500     Xsand_stoneout_1,                   FALSE,  FALSE,
8501     EL_ROCK,                            ACTION_EMPTYING, -1
8502   },
8503   {
8504     Xsand_stoneout_2,                   FALSE,  FALSE,
8505     EL_ROCK,                            ACTION_EMPTYING, -1
8506   },
8507   {
8508     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
8509     EL_QUICKSAND_EMPTYING,              -1, -1
8510   },
8511   {
8512     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
8513     EL_QUICKSAND_EMPTYING,              -1, -1
8514   },
8515
8516   {
8517     Xslide_ns,                          TRUE,   FALSE,
8518     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
8519   },
8520   {
8521     Yslide_ns_blank,                    FALSE,  FALSE,
8522     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
8523   },
8524   {
8525     Xslide_ew,                          TRUE,   FALSE,
8526     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
8527   },
8528   {
8529     Yslide_ew_blank,                    FALSE,  FALSE,
8530     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
8531   },
8532
8533   {
8534     Xwind_n,                            TRUE,   FALSE,
8535     EL_BALLOON_SWITCH_UP,               -1, -1
8536   },
8537   {
8538     Xwind_e,                            TRUE,   FALSE,
8539     EL_BALLOON_SWITCH_RIGHT,            -1, -1
8540   },
8541   {
8542     Xwind_s,                            TRUE,   FALSE,
8543     EL_BALLOON_SWITCH_DOWN,             -1, -1
8544   },
8545   {
8546     Xwind_w,                            TRUE,   FALSE,
8547     EL_BALLOON_SWITCH_LEFT,             -1, -1
8548   },
8549   {
8550     Xwind_any,                          TRUE,   FALSE,
8551     EL_BALLOON_SWITCH_ANY,              -1, -1
8552   },
8553   {
8554     Xwind_stop,                         TRUE,   FALSE,
8555     EL_BALLOON_SWITCH_NONE,             -1, -1
8556   },
8557
8558   {
8559     Xexit,                              TRUE,   FALSE,
8560     EL_EM_EXIT_CLOSED,                  -1, -1
8561   },
8562   {
8563     Xexit_1,                            TRUE,   FALSE,
8564     EL_EM_EXIT_OPEN,                    -1, -1
8565   },
8566   {
8567     Xexit_2,                            FALSE,  FALSE,
8568     EL_EM_EXIT_OPEN,                    -1, -1
8569   },
8570   {
8571     Xexit_3,                            FALSE,  FALSE,
8572     EL_EM_EXIT_OPEN,                    -1, -1
8573   },
8574
8575   {
8576     Xpause,                             FALSE,  FALSE,
8577     EL_EMPTY,                           -1, -1
8578   },
8579
8580   {
8581     Xwall_1,                            TRUE,   FALSE,
8582     EL_WALL,                            -1, -1
8583   },
8584   {
8585     Xwall_2,                            TRUE,   FALSE,
8586     EL_EMC_WALL_14,                     -1, -1
8587   },
8588   {
8589     Xwall_3,                            TRUE,   FALSE,
8590     EL_EMC_WALL_15,                     -1, -1
8591   },
8592   {
8593     Xwall_4,                            TRUE,   FALSE,
8594     EL_EMC_WALL_16,                     -1, -1
8595   },
8596
8597   {
8598     Xroundwall_1,                       TRUE,   FALSE,
8599     EL_WALL_SLIPPERY,                   -1, -1
8600   },
8601   {
8602     Xroundwall_2,                       TRUE,   FALSE,
8603     EL_EMC_WALL_SLIPPERY_2,             -1, -1
8604   },
8605   {
8606     Xroundwall_3,                       TRUE,   FALSE,
8607     EL_EMC_WALL_SLIPPERY_3,             -1, -1
8608   },
8609   {
8610     Xroundwall_4,                       TRUE,   FALSE,
8611     EL_EMC_WALL_SLIPPERY_4,             -1, -1
8612   },
8613
8614   {
8615     Xsteel_1,                           TRUE,   FALSE,
8616     EL_STEELWALL,                       -1, -1
8617   },
8618   {
8619     Xsteel_2,                           TRUE,   FALSE,
8620     EL_EMC_STEELWALL_2,                 -1, -1
8621   },
8622   {
8623     Xsteel_3,                           TRUE,   FALSE,
8624     EL_EMC_STEELWALL_3,                 -1, -1
8625   },
8626   {
8627     Xsteel_4,                           TRUE,   FALSE,
8628     EL_EMC_STEELWALL_4,                 -1, -1
8629   },
8630
8631   {
8632     Xdecor_1,                           TRUE,   FALSE,
8633     EL_EMC_WALL_8,                      -1, -1
8634   },
8635   {
8636     Xdecor_2,                           TRUE,   FALSE,
8637     EL_EMC_WALL_6,                      -1, -1
8638   },
8639   {
8640     Xdecor_3,                           TRUE,   FALSE,
8641     EL_EMC_WALL_4,                      -1, -1
8642   },
8643   {
8644     Xdecor_4,                           TRUE,   FALSE,
8645     EL_EMC_WALL_7,                      -1, -1
8646   },
8647   {
8648     Xdecor_5,                           TRUE,   FALSE,
8649     EL_EMC_WALL_5,                      -1, -1
8650   },
8651   {
8652     Xdecor_6,                           TRUE,   FALSE,
8653     EL_EMC_WALL_9,                      -1, -1
8654   },
8655   {
8656     Xdecor_7,                           TRUE,   FALSE,
8657     EL_EMC_WALL_10,                     -1, -1
8658   },
8659   {
8660     Xdecor_8,                           TRUE,   FALSE,
8661     EL_EMC_WALL_1,                      -1, -1
8662   },
8663   {
8664     Xdecor_9,                           TRUE,   FALSE,
8665     EL_EMC_WALL_2,                      -1, -1
8666   },
8667   {
8668     Xdecor_10,                          TRUE,   FALSE,
8669     EL_EMC_WALL_3,                      -1, -1
8670   },
8671   {
8672     Xdecor_11,                          TRUE,   FALSE,
8673     EL_EMC_WALL_11,                     -1, -1
8674   },
8675   {
8676     Xdecor_12,                          TRUE,   FALSE,
8677     EL_EMC_WALL_12,                     -1, -1
8678   },
8679
8680   {
8681     Xalpha_0,                           TRUE,   FALSE,
8682     EL_CHAR('0'),                       -1, -1
8683   },
8684   {
8685     Xalpha_1,                           TRUE,   FALSE,
8686     EL_CHAR('1'),                       -1, -1
8687   },
8688   {
8689     Xalpha_2,                           TRUE,   FALSE,
8690     EL_CHAR('2'),                       -1, -1
8691   },
8692   {
8693     Xalpha_3,                           TRUE,   FALSE,
8694     EL_CHAR('3'),                       -1, -1
8695   },
8696   {
8697     Xalpha_4,                           TRUE,   FALSE,
8698     EL_CHAR('4'),                       -1, -1
8699   },
8700   {
8701     Xalpha_5,                           TRUE,   FALSE,
8702     EL_CHAR('5'),                       -1, -1
8703   },
8704   {
8705     Xalpha_6,                           TRUE,   FALSE,
8706     EL_CHAR('6'),                       -1, -1
8707   },
8708   {
8709     Xalpha_7,                           TRUE,   FALSE,
8710     EL_CHAR('7'),                       -1, -1
8711   },
8712   {
8713     Xalpha_8,                           TRUE,   FALSE,
8714     EL_CHAR('8'),                       -1, -1
8715   },
8716   {
8717     Xalpha_9,                           TRUE,   FALSE,
8718     EL_CHAR('9'),                       -1, -1
8719   },
8720   {
8721     Xalpha_excla,                       TRUE,   FALSE,
8722     EL_CHAR('!'),                       -1, -1
8723   },
8724   {
8725     Xalpha_apost,                       TRUE,   FALSE,
8726     EL_CHAR('\''),                      -1, -1
8727   },
8728   {
8729     Xalpha_comma,                       TRUE,   FALSE,
8730     EL_CHAR(','),                       -1, -1
8731   },
8732   {
8733     Xalpha_minus,                       TRUE,   FALSE,
8734     EL_CHAR('-'),                       -1, -1
8735   },
8736   {
8737     Xalpha_perio,                       TRUE,   FALSE,
8738     EL_CHAR('.'),                       -1, -1
8739   },
8740   {
8741     Xalpha_colon,                       TRUE,   FALSE,
8742     EL_CHAR(':'),                       -1, -1
8743   },
8744   {
8745     Xalpha_quest,                       TRUE,   FALSE,
8746     EL_CHAR('?'),                       -1, -1
8747   },
8748   {
8749     Xalpha_a,                           TRUE,   FALSE,
8750     EL_CHAR('A'),                       -1, -1
8751   },
8752   {
8753     Xalpha_b,                           TRUE,   FALSE,
8754     EL_CHAR('B'),                       -1, -1
8755   },
8756   {
8757     Xalpha_c,                           TRUE,   FALSE,
8758     EL_CHAR('C'),                       -1, -1
8759   },
8760   {
8761     Xalpha_d,                           TRUE,   FALSE,
8762     EL_CHAR('D'),                       -1, -1
8763   },
8764   {
8765     Xalpha_e,                           TRUE,   FALSE,
8766     EL_CHAR('E'),                       -1, -1
8767   },
8768   {
8769     Xalpha_f,                           TRUE,   FALSE,
8770     EL_CHAR('F'),                       -1, -1
8771   },
8772   {
8773     Xalpha_g,                           TRUE,   FALSE,
8774     EL_CHAR('G'),                       -1, -1
8775   },
8776   {
8777     Xalpha_h,                           TRUE,   FALSE,
8778     EL_CHAR('H'),                       -1, -1
8779   },
8780   {
8781     Xalpha_i,                           TRUE,   FALSE,
8782     EL_CHAR('I'),                       -1, -1
8783   },
8784   {
8785     Xalpha_j,                           TRUE,   FALSE,
8786     EL_CHAR('J'),                       -1, -1
8787   },
8788   {
8789     Xalpha_k,                           TRUE,   FALSE,
8790     EL_CHAR('K'),                       -1, -1
8791   },
8792   {
8793     Xalpha_l,                           TRUE,   FALSE,
8794     EL_CHAR('L'),                       -1, -1
8795   },
8796   {
8797     Xalpha_m,                           TRUE,   FALSE,
8798     EL_CHAR('M'),                       -1, -1
8799   },
8800   {
8801     Xalpha_n,                           TRUE,   FALSE,
8802     EL_CHAR('N'),                       -1, -1
8803   },
8804   {
8805     Xalpha_o,                           TRUE,   FALSE,
8806     EL_CHAR('O'),                       -1, -1
8807   },
8808   {
8809     Xalpha_p,                           TRUE,   FALSE,
8810     EL_CHAR('P'),                       -1, -1
8811   },
8812   {
8813     Xalpha_q,                           TRUE,   FALSE,
8814     EL_CHAR('Q'),                       -1, -1
8815   },
8816   {
8817     Xalpha_r,                           TRUE,   FALSE,
8818     EL_CHAR('R'),                       -1, -1
8819   },
8820   {
8821     Xalpha_s,                           TRUE,   FALSE,
8822     EL_CHAR('S'),                       -1, -1
8823   },
8824   {
8825     Xalpha_t,                           TRUE,   FALSE,
8826     EL_CHAR('T'),                       -1, -1
8827   },
8828   {
8829     Xalpha_u,                           TRUE,   FALSE,
8830     EL_CHAR('U'),                       -1, -1
8831   },
8832   {
8833     Xalpha_v,                           TRUE,   FALSE,
8834     EL_CHAR('V'),                       -1, -1
8835   },
8836   {
8837     Xalpha_w,                           TRUE,   FALSE,
8838     EL_CHAR('W'),                       -1, -1
8839   },
8840   {
8841     Xalpha_x,                           TRUE,   FALSE,
8842     EL_CHAR('X'),                       -1, -1
8843   },
8844   {
8845     Xalpha_y,                           TRUE,   FALSE,
8846     EL_CHAR('Y'),                       -1, -1
8847   },
8848   {
8849     Xalpha_z,                           TRUE,   FALSE,
8850     EL_CHAR('Z'),                       -1, -1
8851   },
8852   {
8853     Xalpha_arrow_e,                     TRUE,   FALSE,
8854     EL_CHAR('>'),                       -1, -1
8855   },
8856   {
8857     Xalpha_arrow_w,                     TRUE,   FALSE,
8858     EL_CHAR('<'),                       -1, -1
8859   },
8860   {
8861     Xalpha_copyr,                       TRUE,   FALSE,
8862     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
8863   },
8864
8865   {
8866     Ykey_1_blank,                       FALSE,  FALSE,
8867     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
8868   },
8869   {
8870     Ykey_2_blank,                       FALSE,  FALSE,
8871     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
8872   },
8873   {
8874     Ykey_3_blank,                       FALSE,  FALSE,
8875     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
8876   },
8877   {
8878     Ykey_4_blank,                       FALSE,  FALSE,
8879     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
8880   },
8881   {
8882     Ykey_5_blank,                       FALSE,  FALSE,
8883     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
8884   },
8885   {
8886     Ykey_6_blank,                       FALSE,  FALSE,
8887     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
8888   },
8889   {
8890     Ykey_7_blank,                       FALSE,  FALSE,
8891     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
8892   },
8893   {
8894     Ykey_8_blank,                       FALSE,  FALSE,
8895     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
8896   },
8897   {
8898     Ylenses_blank,                      FALSE,  FALSE,
8899     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
8900   },
8901   {
8902     Ymagnify_blank,                     FALSE,  FALSE,
8903     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
8904   },
8905   {
8906     Ygrass_blank,                       FALSE,  FALSE,
8907     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
8908   },
8909   {
8910     Ydirt_blank,                        FALSE,  FALSE,
8911     EL_SAND,                            ACTION_SNAPPING, -1
8912   },
8913
8914   {
8915     -1,                                 FALSE,  FALSE,
8916     -1,                                 -1, -1
8917   }
8918 };
8919
8920 static struct Mapping_EM_to_RND_player
8921 {
8922   int action_em;
8923   int player_nr;
8924
8925   int element_rnd;
8926   int action;
8927   int direction;
8928 }
8929 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
8930 {
8931   {
8932     PLY_walk_n,                         0,
8933     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
8934   },
8935   {
8936     PLY_walk_e,                         0,
8937     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
8938   },
8939   {
8940     PLY_walk_s,                         0,
8941     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
8942   },
8943   {
8944     PLY_walk_w,                         0,
8945     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
8946   },
8947   {
8948     PLY_push_n,                         0,
8949     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
8950   },
8951   {
8952     PLY_push_e,                         0,
8953     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
8954   },
8955   {
8956     PLY_push_s,                         0,
8957     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
8958   },
8959   {
8960     PLY_push_w,                         0,
8961     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
8962   },
8963   {
8964     PLY_shoot_n,                        0,
8965     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
8966   },
8967   {
8968     PLY_shoot_e,                        0,
8969     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
8970   },
8971   {
8972     PLY_shoot_s,                        0,
8973     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
8974   },
8975   {
8976     PLY_shoot_w,                        0,
8977     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
8978   },
8979   {
8980     PLY_walk_n,                         1,
8981     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
8982   },
8983   {
8984     PLY_walk_e,                         1,
8985     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
8986   },
8987   {
8988     PLY_walk_s,                         1,
8989     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
8990   },
8991   {
8992     PLY_walk_w,                         1,
8993     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
8994   },
8995   {
8996     PLY_push_n,                         1,
8997     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
8998   },
8999   {
9000     PLY_push_e,                         1,
9001     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
9002   },
9003   {
9004     PLY_push_s,                         1,
9005     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
9006   },
9007   {
9008     PLY_push_w,                         1,
9009     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
9010   },
9011   {
9012     PLY_shoot_n,                        1,
9013     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
9014   },
9015   {
9016     PLY_shoot_e,                        1,
9017     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
9018   },
9019   {
9020     PLY_shoot_s,                        1,
9021     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
9022   },
9023   {
9024     PLY_shoot_w,                        1,
9025     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
9026   },
9027   {
9028     PLY_still,                          0,
9029     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
9030   },
9031   {
9032     PLY_still,                          1,
9033     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
9034   },
9035   {
9036     PLY_walk_n,                         2,
9037     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
9038   },
9039   {
9040     PLY_walk_e,                         2,
9041     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
9042   },
9043   {
9044     PLY_walk_s,                         2,
9045     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
9046   },
9047   {
9048     PLY_walk_w,                         2,
9049     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
9050   },
9051   {
9052     PLY_push_n,                         2,
9053     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
9054   },
9055   {
9056     PLY_push_e,                         2,
9057     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
9058   },
9059   {
9060     PLY_push_s,                         2,
9061     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
9062   },
9063   {
9064     PLY_push_w,                         2,
9065     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
9066   },
9067   {
9068     PLY_shoot_n,                        2,
9069     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
9070   },
9071   {
9072     PLY_shoot_e,                        2,
9073     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
9074   },
9075   {
9076     PLY_shoot_s,                        2,
9077     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
9078   },
9079   {
9080     PLY_shoot_w,                        2,
9081     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
9082   },
9083   {
9084     PLY_walk_n,                         3,
9085     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
9086   },
9087   {
9088     PLY_walk_e,                         3,
9089     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
9090   },
9091   {
9092     PLY_walk_s,                         3,
9093     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
9094   },
9095   {
9096     PLY_walk_w,                         3,
9097     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
9098   },
9099   {
9100     PLY_push_n,                         3,
9101     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
9102   },
9103   {
9104     PLY_push_e,                         3,
9105     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
9106   },
9107   {
9108     PLY_push_s,                         3,
9109     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
9110   },
9111   {
9112     PLY_push_w,                         3,
9113     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
9114   },
9115   {
9116     PLY_shoot_n,                        3,
9117     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
9118   },
9119   {
9120     PLY_shoot_e,                        3,
9121     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
9122   },
9123   {
9124     PLY_shoot_s,                        3,
9125     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
9126   },
9127   {
9128     PLY_shoot_w,                        3,
9129     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
9130   },
9131   {
9132     PLY_still,                          2,
9133     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
9134   },
9135   {
9136     PLY_still,                          3,
9137     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
9138   },
9139
9140   {
9141     -1,                                 -1,
9142     -1,                                 -1, -1
9143   }
9144 };
9145
9146 int map_element_RND_to_EM_cave(int element_rnd)
9147 {
9148   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
9149   static boolean mapping_initialized = FALSE;
9150
9151   if (!mapping_initialized)
9152   {
9153     int i;
9154
9155     // return "Xalpha_quest" for all undefined elements in mapping array
9156     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9157       mapping_RND_to_EM[i] = Xalpha_quest;
9158
9159     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9160       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
9161         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
9162           em_object_mapping_list[i].element_em;
9163
9164     mapping_initialized = TRUE;
9165   }
9166
9167   if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
9168   {
9169     Warn("invalid RND level element %d", element_rnd);
9170
9171     return EL_UNKNOWN;
9172   }
9173
9174   return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
9175 }
9176
9177 int map_element_EM_to_RND_cave(int element_em_cave)
9178 {
9179   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9180   static boolean mapping_initialized = FALSE;
9181
9182   if (!mapping_initialized)
9183   {
9184     int i;
9185
9186     // return "EL_UNKNOWN" for all undefined elements in mapping array
9187     for (i = 0; i < GAME_TILE_MAX; i++)
9188       mapping_EM_to_RND[i] = EL_UNKNOWN;
9189
9190     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9191       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9192         em_object_mapping_list[i].element_rnd;
9193
9194     mapping_initialized = TRUE;
9195   }
9196
9197   if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
9198   {
9199     Warn("invalid EM cave element %d", element_em_cave);
9200
9201     return EL_UNKNOWN;
9202   }
9203
9204   return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
9205 }
9206
9207 int map_element_EM_to_RND_game(int element_em_game)
9208 {
9209   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9210   static boolean mapping_initialized = FALSE;
9211
9212   if (!mapping_initialized)
9213   {
9214     int i;
9215
9216     // return "EL_UNKNOWN" for all undefined elements in mapping array
9217     for (i = 0; i < GAME_TILE_MAX; i++)
9218       mapping_EM_to_RND[i] = EL_UNKNOWN;
9219
9220     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9221       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9222         em_object_mapping_list[i].element_rnd;
9223
9224     mapping_initialized = TRUE;
9225   }
9226
9227   if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
9228   {
9229     Warn("invalid EM game element %d", element_em_game);
9230
9231     return EL_UNKNOWN;
9232   }
9233
9234   return mapping_EM_to_RND[element_em_game];
9235 }
9236
9237 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
9238 {
9239   struct LevelInfo_EM *level_em = level->native_em_level;
9240   struct CAVE *cav = level_em->cav;
9241   int i, j;
9242
9243   for (i = 0; i < GAME_TILE_MAX; i++)
9244     cav->android_array[i] = Cblank;
9245
9246   for (i = 0; i < level->num_android_clone_elements; i++)
9247   {
9248     int element_rnd = level->android_clone_element[i];
9249     int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
9250
9251     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
9252       if (em_object_mapping_list[j].element_rnd == element_rnd)
9253         cav->android_array[em_object_mapping_list[j].element_em] =
9254           element_em_cave;
9255   }
9256 }
9257
9258 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
9259 {
9260   struct LevelInfo_EM *level_em = level->native_em_level;
9261   struct CAVE *cav = level_em->cav;
9262   int i, j;
9263
9264   level->num_android_clone_elements = 0;
9265
9266   for (i = 0; i < GAME_TILE_MAX; i++)
9267   {
9268     int element_em_cave = cav->android_array[i];
9269     int element_rnd;
9270     boolean element_found = FALSE;
9271
9272     if (element_em_cave == Cblank)
9273       continue;
9274
9275     element_rnd = map_element_EM_to_RND_cave(element_em_cave);
9276
9277     for (j = 0; j < level->num_android_clone_elements; j++)
9278       if (level->android_clone_element[j] == element_rnd)
9279         element_found = TRUE;
9280
9281     if (!element_found)
9282     {
9283       level->android_clone_element[level->num_android_clone_elements++] =
9284         element_rnd;
9285
9286       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
9287         break;
9288     }
9289   }
9290
9291   if (level->num_android_clone_elements == 0)
9292   {
9293     level->num_android_clone_elements = 1;
9294     level->android_clone_element[0] = EL_EMPTY;
9295   }
9296 }
9297
9298 int map_direction_RND_to_EM(int direction)
9299 {
9300   return (direction == MV_UP    ? 0 :
9301           direction == MV_RIGHT ? 1 :
9302           direction == MV_DOWN  ? 2 :
9303           direction == MV_LEFT  ? 3 :
9304           -1);
9305 }
9306
9307 int map_direction_EM_to_RND(int direction)
9308 {
9309   return (direction == 0 ? MV_UP    :
9310           direction == 1 ? MV_RIGHT :
9311           direction == 2 ? MV_DOWN  :
9312           direction == 3 ? MV_LEFT  :
9313           MV_NONE);
9314 }
9315
9316 int map_element_RND_to_SP(int element_rnd)
9317 {
9318   int element_sp = 0x20;        // map unknown elements to yellow "hardware"
9319
9320   if (element_rnd >= EL_SP_START &&
9321       element_rnd <= EL_SP_END)
9322     element_sp = element_rnd - EL_SP_START;
9323   else if (element_rnd == EL_EMPTY_SPACE)
9324     element_sp = 0x00;
9325   else if (element_rnd == EL_INVISIBLE_WALL)
9326     element_sp = 0x28;
9327
9328   return element_sp;
9329 }
9330
9331 int map_element_SP_to_RND(int element_sp)
9332 {
9333   int element_rnd = EL_UNKNOWN;
9334
9335   if (element_sp >= 0x00 &&
9336       element_sp <= 0x27)
9337     element_rnd = EL_SP_START + element_sp;
9338   else if (element_sp == 0x28)
9339     element_rnd = EL_INVISIBLE_WALL;
9340
9341   return element_rnd;
9342 }
9343
9344 int map_action_SP_to_RND(int action_sp)
9345 {
9346   switch (action_sp)
9347   {
9348     case actActive:             return ACTION_ACTIVE;
9349     case actImpact:             return ACTION_IMPACT;
9350     case actExploding:          return ACTION_EXPLODING;
9351     case actDigging:            return ACTION_DIGGING;
9352     case actSnapping:           return ACTION_SNAPPING;
9353     case actCollecting:         return ACTION_COLLECTING;
9354     case actPassing:            return ACTION_PASSING;
9355     case actPushing:            return ACTION_PUSHING;
9356     case actDropping:           return ACTION_DROPPING;
9357
9358     default:                    return ACTION_DEFAULT;
9359   }
9360 }
9361
9362 int map_element_RND_to_MM(int element_rnd)
9363 {
9364   return (element_rnd >= EL_MM_START_1 &&
9365           element_rnd <= EL_MM_END_1 ?
9366           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
9367
9368           element_rnd >= EL_MM_START_2 &&
9369           element_rnd <= EL_MM_END_2 ?
9370           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
9371
9372           element_rnd >= EL_MM_START_3 &&
9373           element_rnd <= EL_MM_END_3 ?
9374           EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
9375
9376           element_rnd >= EL_CHAR_START &&
9377           element_rnd <= EL_CHAR_END ?
9378           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
9379
9380           element_rnd >= EL_MM_RUNTIME_START &&
9381           element_rnd <= EL_MM_RUNTIME_END ?
9382           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
9383
9384           EL_MM_EMPTY_NATIVE);
9385 }
9386
9387 int map_element_MM_to_RND(int element_mm)
9388 {
9389   return (element_mm == EL_MM_EMPTY_NATIVE ||
9390           element_mm == EL_DF_EMPTY_NATIVE ?
9391           EL_EMPTY :
9392
9393           element_mm >= EL_MM_START_1_NATIVE &&
9394           element_mm <= EL_MM_END_1_NATIVE ?
9395           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
9396
9397           element_mm >= EL_MM_START_2_NATIVE &&
9398           element_mm <= EL_MM_END_2_NATIVE ?
9399           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
9400
9401           element_mm >= EL_MM_START_3_NATIVE &&
9402           element_mm <= EL_MM_END_3_NATIVE ?
9403           EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
9404
9405           element_mm >= EL_MM_CHAR_START_NATIVE &&
9406           element_mm <= EL_MM_CHAR_END_NATIVE ?
9407           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
9408
9409           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
9410           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
9411           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
9412
9413           EL_EMPTY);
9414 }
9415
9416 int map_action_MM_to_RND(int action_mm)
9417 {
9418   // all MM actions are defined to exactly match their RND counterparts
9419   return action_mm;
9420 }
9421
9422 int map_sound_MM_to_RND(int sound_mm)
9423 {
9424   switch (sound_mm)
9425   {
9426     case SND_MM_GAME_LEVELTIME_CHARGING:
9427       return SND_GAME_LEVELTIME_CHARGING;
9428
9429     case SND_MM_GAME_HEALTH_CHARGING:
9430       return SND_GAME_HEALTH_CHARGING;
9431
9432     default:
9433       return SND_UNDEFINED;
9434   }
9435 }
9436
9437 int map_mm_wall_element(int element)
9438 {
9439   return (element >= EL_MM_STEEL_WALL_START &&
9440           element <= EL_MM_STEEL_WALL_END ?
9441           EL_MM_STEEL_WALL :
9442
9443           element >= EL_MM_WOODEN_WALL_START &&
9444           element <= EL_MM_WOODEN_WALL_END ?
9445           EL_MM_WOODEN_WALL :
9446
9447           element >= EL_MM_ICE_WALL_START &&
9448           element <= EL_MM_ICE_WALL_END ?
9449           EL_MM_ICE_WALL :
9450
9451           element >= EL_MM_AMOEBA_WALL_START &&
9452           element <= EL_MM_AMOEBA_WALL_END ?
9453           EL_MM_AMOEBA_WALL :
9454
9455           element >= EL_DF_STEEL_WALL_START &&
9456           element <= EL_DF_STEEL_WALL_END ?
9457           EL_DF_STEEL_WALL :
9458
9459           element >= EL_DF_WOODEN_WALL_START &&
9460           element <= EL_DF_WOODEN_WALL_END ?
9461           EL_DF_WOODEN_WALL :
9462
9463           element);
9464 }
9465
9466 int map_mm_wall_element_editor(int element)
9467 {
9468   switch (element)
9469   {
9470     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
9471     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
9472     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
9473     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
9474     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
9475     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
9476
9477     default:                    return element;
9478   }
9479 }
9480
9481 int get_next_element(int element)
9482 {
9483   switch (element)
9484   {
9485     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
9486     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
9487     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
9488     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
9489     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
9490     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
9491     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
9492     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
9493     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
9494     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
9495     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
9496
9497     default:                            return element;
9498   }
9499 }
9500
9501 int el2img_mm(int element_mm)
9502 {
9503   return el2img(map_element_MM_to_RND(element_mm));
9504 }
9505
9506 int el_act2img_mm(int element_mm, int action)
9507 {
9508   return el_act2img(map_element_MM_to_RND(element_mm), action);
9509 }
9510
9511 int el_act_dir2img(int element, int action, int direction)
9512 {
9513   element = GFX_ELEMENT(element);
9514   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
9515
9516   // direction_graphic[][] == graphic[] for undefined direction graphics
9517   return element_info[element].direction_graphic[action][direction];
9518 }
9519
9520 static int el_act_dir2crm(int element, int action, int direction)
9521 {
9522   element = GFX_ELEMENT(element);
9523   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
9524
9525   // direction_graphic[][] == graphic[] for undefined direction graphics
9526   return element_info[element].direction_crumbled[action][direction];
9527 }
9528
9529 int el_act2img(int element, int action)
9530 {
9531   element = GFX_ELEMENT(element);
9532
9533   return element_info[element].graphic[action];
9534 }
9535
9536 int el_act2crm(int element, int action)
9537 {
9538   element = GFX_ELEMENT(element);
9539
9540   return element_info[element].crumbled[action];
9541 }
9542
9543 int el_dir2img(int element, int direction)
9544 {
9545   element = GFX_ELEMENT(element);
9546
9547   return el_act_dir2img(element, ACTION_DEFAULT, direction);
9548 }
9549
9550 int el2baseimg(int element)
9551 {
9552   return element_info[element].graphic[ACTION_DEFAULT];
9553 }
9554
9555 int el2img(int element)
9556 {
9557   element = GFX_ELEMENT(element);
9558
9559   return element_info[element].graphic[ACTION_DEFAULT];
9560 }
9561
9562 int el2edimg(int element)
9563 {
9564   element = GFX_ELEMENT(element);
9565
9566   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
9567 }
9568
9569 int el2preimg(int element)
9570 {
9571   element = GFX_ELEMENT(element);
9572
9573   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
9574 }
9575
9576 int el2panelimg(int element)
9577 {
9578   element = GFX_ELEMENT(element);
9579
9580   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
9581 }
9582
9583 int font2baseimg(int font_nr)
9584 {
9585   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
9586 }
9587
9588 int getBeltNrFromBeltElement(int element)
9589 {
9590   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
9591           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
9592           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
9593 }
9594
9595 int getBeltNrFromBeltActiveElement(int element)
9596 {
9597   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
9598           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
9599           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
9600 }
9601
9602 int getBeltNrFromBeltSwitchElement(int element)
9603 {
9604   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
9605           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
9606           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
9607 }
9608
9609 int getBeltDirNrFromBeltElement(int element)
9610 {
9611   static int belt_base_element[4] =
9612   {
9613     EL_CONVEYOR_BELT_1_LEFT,
9614     EL_CONVEYOR_BELT_2_LEFT,
9615     EL_CONVEYOR_BELT_3_LEFT,
9616     EL_CONVEYOR_BELT_4_LEFT
9617   };
9618
9619   int belt_nr = getBeltNrFromBeltElement(element);
9620   int belt_dir_nr = element - belt_base_element[belt_nr];
9621
9622   return (belt_dir_nr % 3);
9623 }
9624
9625 int getBeltDirNrFromBeltSwitchElement(int element)
9626 {
9627   static int belt_base_element[4] =
9628   {
9629     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
9630     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
9631     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
9632     EL_CONVEYOR_BELT_4_SWITCH_LEFT
9633   };
9634
9635   int belt_nr = getBeltNrFromBeltSwitchElement(element);
9636   int belt_dir_nr = element - belt_base_element[belt_nr];
9637
9638   return (belt_dir_nr % 3);
9639 }
9640
9641 int getBeltDirFromBeltElement(int element)
9642 {
9643   static int belt_move_dir[3] =
9644   {
9645     MV_LEFT,
9646     MV_NONE,
9647     MV_RIGHT
9648   };
9649
9650   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
9651
9652   return belt_move_dir[belt_dir_nr];
9653 }
9654
9655 int getBeltDirFromBeltSwitchElement(int element)
9656 {
9657   static int belt_move_dir[3] =
9658   {
9659     MV_LEFT,
9660     MV_NONE,
9661     MV_RIGHT
9662   };
9663
9664   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
9665
9666   return belt_move_dir[belt_dir_nr];
9667 }
9668
9669 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
9670 {
9671   static int belt_base_element[4] =
9672   {
9673     EL_CONVEYOR_BELT_1_LEFT,
9674     EL_CONVEYOR_BELT_2_LEFT,
9675     EL_CONVEYOR_BELT_3_LEFT,
9676     EL_CONVEYOR_BELT_4_LEFT
9677   };
9678
9679   return belt_base_element[belt_nr] + belt_dir_nr;
9680 }
9681
9682 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
9683 {
9684   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
9685
9686   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
9687 }
9688
9689 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
9690 {
9691   static int belt_base_element[4] =
9692   {
9693     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
9694     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
9695     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
9696     EL_CONVEYOR_BELT_4_SWITCH_LEFT
9697   };
9698
9699   return belt_base_element[belt_nr] + belt_dir_nr;
9700 }
9701
9702 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
9703 {
9704   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
9705
9706   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
9707 }
9708
9709 boolean swapTiles_EM(boolean is_pre_emc_cave)
9710 {
9711   return is_pre_emc_cave && leveldir_current->use_emc_tiles;
9712 }
9713
9714 boolean getTeamMode_EM(void)
9715 {
9716   return game.team_mode || network_playing;
9717 }
9718
9719 boolean isActivePlayer_EM(int player_nr)
9720 {
9721   return stored_player[player_nr].active;
9722 }
9723
9724 unsigned int InitRND(int seed)
9725 {
9726   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
9727     return InitEngineRandom_BD(seed);
9728   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9729     return InitEngineRandom_EM(seed);
9730   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
9731     return InitEngineRandom_SP(seed);
9732   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
9733     return InitEngineRandom_MM(seed);
9734   else
9735     return InitEngineRandom_RND(seed);
9736 }
9737
9738 static struct Mapping_BD_to_RND_object bd_object_mapping[O_MAX_ALL];
9739 static struct Mapping_EM_to_RND_object em_object_mapping[GAME_TILE_MAX];
9740 static struct Mapping_EM_to_RND_player em_player_mapping[MAX_PLAYERS][PLY_MAX];
9741
9742 static int get_effective_element_EM(int tile, int frame_em)
9743 {
9744   int element             = em_object_mapping[tile].element_rnd;
9745   int action              = em_object_mapping[tile].action;
9746   boolean is_backside     = em_object_mapping[tile].is_backside;
9747   boolean action_removing = (action == ACTION_DIGGING ||
9748                              action == ACTION_SNAPPING ||
9749                              action == ACTION_COLLECTING);
9750
9751   if (frame_em < 7)
9752   {
9753     switch (tile)
9754     {
9755       case Xsplash_e:
9756       case Xsplash_w:
9757         return (frame_em > 5 ? EL_EMPTY : element);
9758
9759       default:
9760         return element;
9761     }
9762   }
9763   else  // frame_em == 7
9764   {
9765     switch (tile)
9766     {
9767       case Xsplash_e:
9768       case Xsplash_w:
9769         return EL_EMPTY;
9770
9771       case Ynut_stone:
9772         return EL_EMERALD;
9773
9774       case Ydiamond_stone:
9775         return EL_ROCK;
9776
9777       case Xdrip_stretch:
9778       case Xdrip_stretchB:
9779       case Ydrip_1_s:
9780       case Ydrip_1_sB:
9781       case Yball_1:
9782       case Xball_2:
9783       case Yball_2:
9784       case Yball_blank:
9785       case Ykey_1_blank:
9786       case Ykey_2_blank:
9787       case Ykey_3_blank:
9788       case Ykey_4_blank:
9789       case Ykey_5_blank:
9790       case Ykey_6_blank:
9791       case Ykey_7_blank:
9792       case Ykey_8_blank:
9793       case Ylenses_blank:
9794       case Ymagnify_blank:
9795       case Ygrass_blank:
9796       case Ydirt_blank:
9797       case Xsand_stonein_1:
9798       case Xsand_stonein_2:
9799       case Xsand_stonein_3:
9800       case Xsand_stonein_4:
9801         return element;
9802
9803       default:
9804         return (is_backside || action_removing ? EL_EMPTY : element);
9805     }
9806   }
9807 }
9808
9809 static boolean check_linear_animation_EM(int tile)
9810 {
9811   switch (tile)
9812   {
9813     case Xsand_stonesand_1:
9814     case Xsand_stonesand_quickout_1:
9815     case Xsand_sandstone_1:
9816     case Xsand_stonein_1:
9817     case Xsand_stoneout_1:
9818     case Xboom_1:
9819     case Xdynamite_1:
9820     case Ybug_w_n:
9821     case Ybug_n_e:
9822     case Ybug_e_s:
9823     case Ybug_s_w:
9824     case Ybug_e_n:
9825     case Ybug_s_e:
9826     case Ybug_w_s:
9827     case Ybug_n_w:
9828     case Ytank_w_n:
9829     case Ytank_n_e:
9830     case Ytank_e_s:
9831     case Ytank_s_w:
9832     case Ytank_e_n:
9833     case Ytank_s_e:
9834     case Ytank_w_s:
9835     case Ytank_n_w:
9836     case Xsplash_e:
9837     case Xsplash_w:
9838     case Ynut_stone:
9839       return TRUE;
9840   }
9841
9842   return FALSE;
9843 }
9844
9845 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
9846                                      boolean has_crumbled_graphics,
9847                                      int crumbled, int sync_frame)
9848 {
9849   // if element can be crumbled, but certain action graphics are just empty
9850   // space (like instantly snapping sand to empty space in 1 frame), do not
9851   // treat these empty space graphics as crumbled graphics in EMC engine
9852   if (crumbled == IMG_EMPTY_SPACE)
9853     has_crumbled_graphics = FALSE;
9854
9855   if (has_crumbled_graphics)
9856   {
9857     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
9858     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
9859                                            g_crumbled->anim_delay,
9860                                            g_crumbled->anim_mode,
9861                                            g_crumbled->anim_start_frame,
9862                                            sync_frame);
9863
9864     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
9865                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
9866
9867     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
9868     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
9869
9870     g_em->has_crumbled_graphics = TRUE;
9871   }
9872   else
9873   {
9874     g_em->crumbled_bitmap = NULL;
9875     g_em->crumbled_src_x = 0;
9876     g_em->crumbled_src_y = 0;
9877     g_em->crumbled_border_size = 0;
9878     g_em->crumbled_tile_size = 0;
9879
9880     g_em->has_crumbled_graphics = FALSE;
9881   }
9882 }
9883
9884 #if 0
9885 void ResetGfxAnimation_EM(int x, int y, int tile)
9886 {
9887   GfxFrame[x][y] = 0;
9888 }
9889 #endif
9890
9891 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
9892                         int tile, int frame_em, int x, int y)
9893 {
9894   int action = em_object_mapping[tile].action;
9895   int direction = em_object_mapping[tile].direction;
9896   int effective_element = get_effective_element_EM(tile, frame_em);
9897   int graphic = (direction == MV_NONE ?
9898                  el_act2img(effective_element, action) :
9899                  el_act_dir2img(effective_element, action, direction));
9900   struct GraphicInfo *g = &graphic_info[graphic];
9901   int sync_frame;
9902   boolean action_removing = (action == ACTION_DIGGING ||
9903                              action == ACTION_SNAPPING ||
9904                              action == ACTION_COLLECTING);
9905   boolean action_moving   = (action == ACTION_FALLING ||
9906                              action == ACTION_MOVING ||
9907                              action == ACTION_PUSHING ||
9908                              action == ACTION_EATING ||
9909                              action == ACTION_FILLING ||
9910                              action == ACTION_EMPTYING);
9911   boolean action_falling  = (action == ACTION_FALLING ||
9912                              action == ACTION_FILLING ||
9913                              action == ACTION_EMPTYING);
9914
9915   // special case: graphic uses "2nd movement tile" and has defined
9916   // 7 frames for movement animation (or less) => use default graphic
9917   // for last (8th) frame which ends the movement animation
9918   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
9919   {
9920     action = ACTION_DEFAULT;    // (keep action_* unchanged for now)
9921     graphic = (direction == MV_NONE ?
9922                el_act2img(effective_element, action) :
9923                el_act_dir2img(effective_element, action, direction));
9924
9925     g = &graphic_info[graphic];
9926   }
9927
9928   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
9929   {
9930     GfxFrame[x][y] = 0;
9931   }
9932   else if (action_moving)
9933   {
9934     boolean is_backside = em_object_mapping[tile].is_backside;
9935
9936     if (is_backside)
9937     {
9938       int direction = em_object_mapping[tile].direction;
9939       int move_dir = (action_falling ? MV_DOWN : direction);
9940
9941       GfxFrame[x][y]++;
9942
9943 #if 1
9944       // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
9945       if (g->double_movement && frame_em == 0)
9946         GfxFrame[x][y] = 0;
9947 #endif
9948
9949       if (move_dir == MV_LEFT)
9950         GfxFrame[x - 1][y] = GfxFrame[x][y];
9951       else if (move_dir == MV_RIGHT)
9952         GfxFrame[x + 1][y] = GfxFrame[x][y];
9953       else if (move_dir == MV_UP)
9954         GfxFrame[x][y - 1] = GfxFrame[x][y];
9955       else if (move_dir == MV_DOWN)
9956         GfxFrame[x][y + 1] = GfxFrame[x][y];
9957     }
9958   }
9959   else
9960   {
9961     GfxFrame[x][y]++;
9962
9963     // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
9964     if (tile == Xsand_stonesand_quickout_1 ||
9965         tile == Xsand_stonesand_quickout_2)
9966       GfxFrame[x][y]++;
9967   }
9968
9969   if (graphic_info[graphic].anim_global_sync)
9970     sync_frame = FrameCounter;
9971   else if (graphic_info[graphic].anim_global_anim_sync)
9972     sync_frame = getGlobalAnimSyncFrame();
9973   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
9974     sync_frame = GfxFrame[x][y];
9975   else
9976     sync_frame = 0;     // playfield border (pseudo steel)
9977
9978   SetRandomAnimationValue(x, y);
9979
9980   int frame = getAnimationFrame(g->anim_frames,
9981                                 g->anim_delay,
9982                                 g->anim_mode,
9983                                 g->anim_start_frame,
9984                                 sync_frame);
9985
9986   g_em->unique_identifier =
9987     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
9988 }
9989
9990 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
9991                                   int tile, int frame_em, int x, int y)
9992 {
9993   int action = em_object_mapping[tile].action;
9994   int direction = em_object_mapping[tile].direction;
9995   boolean is_backside = em_object_mapping[tile].is_backside;
9996   int effective_element = get_effective_element_EM(tile, frame_em);
9997   int effective_action = action;
9998   int graphic = (direction == MV_NONE ?
9999                  el_act2img(effective_element, effective_action) :
10000                  el_act_dir2img(effective_element, effective_action,
10001                                 direction));
10002   int crumbled = (direction == MV_NONE ?
10003                   el_act2crm(effective_element, effective_action) :
10004                   el_act_dir2crm(effective_element, effective_action,
10005                                  direction));
10006   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10007   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10008   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10009   struct GraphicInfo *g = &graphic_info[graphic];
10010   int sync_frame;
10011
10012   // special case: graphic uses "2nd movement tile" and has defined
10013   // 7 frames for movement animation (or less) => use default graphic
10014   // for last (8th) frame which ends the movement animation
10015   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
10016   {
10017     effective_action = ACTION_DEFAULT;
10018     graphic = (direction == MV_NONE ?
10019                el_act2img(effective_element, effective_action) :
10020                el_act_dir2img(effective_element, effective_action,
10021                               direction));
10022     crumbled = (direction == MV_NONE ?
10023                 el_act2crm(effective_element, effective_action) :
10024                 el_act_dir2crm(effective_element, effective_action,
10025                                direction));
10026
10027     g = &graphic_info[graphic];
10028   }
10029
10030   if (graphic_info[graphic].anim_global_sync)
10031     sync_frame = FrameCounter;
10032   else if (graphic_info[graphic].anim_global_anim_sync)
10033     sync_frame = getGlobalAnimSyncFrame();
10034   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
10035     sync_frame = GfxFrame[x][y];
10036   else
10037     sync_frame = 0;     // playfield border (pseudo steel)
10038
10039   SetRandomAnimationValue(x, y);
10040
10041   int frame = getAnimationFrame(g->anim_frames,
10042                                 g->anim_delay,
10043                                 g->anim_mode,
10044                                 g->anim_start_frame,
10045                                 sync_frame);
10046
10047   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
10048                       g->double_movement && is_backside);
10049
10050   // (updating the "crumbled" graphic definitions is probably not really needed,
10051   // as animations for crumbled graphics can't be longer than one EMC cycle)
10052   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10053                            sync_frame);
10054 }
10055
10056 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
10057                                   int player_nr, int anim, int frame_em)
10058 {
10059   int element   = em_player_mapping[player_nr][anim].element_rnd;
10060   int action    = em_player_mapping[player_nr][anim].action;
10061   int direction = em_player_mapping[player_nr][anim].direction;
10062   int graphic = (direction == MV_NONE ?
10063                  el_act2img(element, action) :
10064                  el_act_dir2img(element, action, direction));
10065   struct GraphicInfo *g = &graphic_info[graphic];
10066   int sync_frame;
10067
10068   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
10069
10070   stored_player[player_nr].StepFrame = frame_em;
10071
10072   sync_frame = stored_player[player_nr].Frame;
10073
10074   int frame = getAnimationFrame(g->anim_frames,
10075                                 g->anim_delay,
10076                                 g->anim_mode,
10077                                 g->anim_start_frame,
10078                                 sync_frame);
10079
10080   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
10081                       &g_em->src_x, &g_em->src_y, FALSE);
10082 }
10083
10084 #define BD_GFX_RANGE(a, n, i)           ((i) >= (a) && (i) < (a) + (n))
10085 #define BD_GFX_FRAME(b, i)              (((i) - (b)) * 8)
10086
10087 void InitGraphicInfo_BD(void)
10088 {
10089   int i, j;
10090
10091   // always start with reliable default values
10092   for (i = 0; i < O_MAX_ALL; i++)
10093   {
10094     bd_object_mapping[i].element_rnd = EL_UNKNOWN;
10095     bd_object_mapping[i].action = ACTION_DEFAULT;
10096     bd_object_mapping[i].direction = MV_NONE;
10097   }
10098
10099   for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
10100   {
10101     int e = bd_object_mapping_list[i].element_bd;
10102
10103     bd_object_mapping[e].element_rnd = bd_object_mapping_list[i].element_rnd;
10104
10105     if (bd_object_mapping_list[i].action != -1)
10106       bd_object_mapping[e].action = bd_object_mapping_list[i].action;
10107
10108     if (bd_object_mapping_list[i].direction != -1)
10109       bd_object_mapping[e].direction =
10110         MV_DIR_FROM_BIT(bd_object_mapping_list[i].direction);
10111   }
10112
10113   for (i = 0; i < O_MAX_ALL; i++)
10114   {
10115     int element = bd_object_mapping[i].element_rnd;
10116     int action = bd_object_mapping[i].action;
10117     int direction = bd_object_mapping[i].direction;
10118
10119     for (j = 0; j < 8; j++)
10120     {
10121       int effective_element = element;
10122       int effective_action = action;
10123       int graphic = (el_act_dir2img(effective_element, effective_action,
10124                                     direction));
10125       struct GraphicInfo *g = &graphic_info[graphic];
10126       struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[i][j];
10127       Bitmap *src_bitmap;
10128       int src_x, src_y;
10129       int sync_frame = (BD_GFX_RANGE(O_PRE_PL_1, 3, i)        ? BD_GFX_FRAME(O_PRE_PL_1, i) :
10130                         BD_GFX_RANGE(O_PRE_DIA_1, 5, i)       ? BD_GFX_FRAME(O_PRE_DIA_1, i) :
10131                         BD_GFX_RANGE(O_PRE_STONE_1, 4, i)     ? BD_GFX_FRAME(O_PRE_STONE_1, i) :
10132                         BD_GFX_RANGE(O_PRE_STEEL_1, 4, i)     ? BD_GFX_FRAME(O_PRE_STEEL_1, i) :
10133                         BD_GFX_RANGE(O_BOMB_TICK_1, 7, i)     ? BD_GFX_FRAME(O_BOMB_TICK_1, i) :
10134                         BD_GFX_RANGE(O_BOMB_EXPL_1, 4, i)     ? BD_GFX_FRAME(O_BOMB_EXPL_1, i) :
10135                         BD_GFX_RANGE(O_NUT_EXPL_1, 4, i)      ? BD_GFX_FRAME(O_NUT_EXPL_1, i) :
10136                         BD_GFX_RANGE(O_GHOST_EXPL_1, 4, i)    ? BD_GFX_FRAME(O_GHOST_EXPL_1, i) :
10137                         BD_GFX_RANGE(O_EXPLODE_1, 5, i)       ? BD_GFX_FRAME(O_EXPLODE_1, i) :
10138                         BD_GFX_RANGE(O_PRE_CLOCK_1, 4, i)     ? BD_GFX_FRAME(O_PRE_CLOCK_1, i) :
10139                         BD_GFX_RANGE(O_NITRO_EXPL_1, 4, i)    ? BD_GFX_FRAME(O_NITRO_EXPL_1, i) :
10140                         BD_GFX_RANGE(O_AMOEBA_2_EXPL_1, 4, i) ? BD_GFX_FRAME(O_AMOEBA_2_EXPL_1, i):
10141                         i == O_INBOX_OPEN || i == O_OUTBOX_OPEN ? j :
10142                         j * 2);
10143       int frame = getAnimationFrame(g->anim_frames,
10144                                     g->anim_delay,
10145                                     g->anim_mode,
10146                                     g->anim_start_frame,
10147                                     sync_frame);
10148
10149       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10150
10151       g_bd->bitmap = src_bitmap;
10152       g_bd->src_x  = src_x;
10153       g_bd->src_y  = src_y;
10154       g_bd->width  = TILEX;
10155       g_bd->height = TILEY;
10156     }
10157   }
10158 }
10159
10160 void InitGraphicInfo_EM(void)
10161 {
10162   int i, j, p;
10163
10164   // always start with reliable default values
10165   for (i = 0; i < GAME_TILE_MAX; i++)
10166   {
10167     em_object_mapping[i].element_rnd = EL_UNKNOWN;
10168     em_object_mapping[i].is_backside = FALSE;
10169     em_object_mapping[i].action = ACTION_DEFAULT;
10170     em_object_mapping[i].direction = MV_NONE;
10171   }
10172
10173   // always start with reliable default values
10174   for (p = 0; p < MAX_PLAYERS; p++)
10175   {
10176     for (i = 0; i < PLY_MAX; i++)
10177     {
10178       em_player_mapping[p][i].element_rnd = EL_UNKNOWN;
10179       em_player_mapping[p][i].action = ACTION_DEFAULT;
10180       em_player_mapping[p][i].direction = MV_NONE;
10181     }
10182   }
10183
10184   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
10185   {
10186     int e = em_object_mapping_list[i].element_em;
10187
10188     em_object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
10189     em_object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
10190
10191     if (em_object_mapping_list[i].action != -1)
10192       em_object_mapping[e].action = em_object_mapping_list[i].action;
10193
10194     if (em_object_mapping_list[i].direction != -1)
10195       em_object_mapping[e].direction =
10196         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
10197   }
10198
10199   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
10200   {
10201     int a = em_player_mapping_list[i].action_em;
10202     int p = em_player_mapping_list[i].player_nr;
10203
10204     em_player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
10205
10206     if (em_player_mapping_list[i].action != -1)
10207       em_player_mapping[p][a].action = em_player_mapping_list[i].action;
10208
10209     if (em_player_mapping_list[i].direction != -1)
10210       em_player_mapping[p][a].direction =
10211         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
10212   }
10213
10214   for (i = 0; i < GAME_TILE_MAX; i++)
10215   {
10216     int element = em_object_mapping[i].element_rnd;
10217     int action = em_object_mapping[i].action;
10218     int direction = em_object_mapping[i].direction;
10219     boolean is_backside = em_object_mapping[i].is_backside;
10220     boolean action_exploding = ((action == ACTION_EXPLODING ||
10221                                  action == ACTION_SMASHED_BY_ROCK ||
10222                                  action == ACTION_SMASHED_BY_SPRING) &&
10223                                 element != EL_DIAMOND);
10224     boolean action_active = (action == ACTION_ACTIVE);
10225     boolean action_other = (action == ACTION_OTHER);
10226
10227     for (j = 0; j < 8; j++)
10228     {
10229       int effective_element = get_effective_element_EM(i, j);
10230       int effective_action = (j < 7 ? action :
10231                               i == Xdrip_stretch ? action :
10232                               i == Xdrip_stretchB ? action :
10233                               i == Ydrip_1_s ? action :
10234                               i == Ydrip_1_sB ? action :
10235                               i == Yball_1 ? action :
10236                               i == Xball_2 ? action :
10237                               i == Yball_2 ? action :
10238                               i == Yball_blank ? action :
10239                               i == Ykey_1_blank ? action :
10240                               i == Ykey_2_blank ? action :
10241                               i == Ykey_3_blank ? action :
10242                               i == Ykey_4_blank ? action :
10243                               i == Ykey_5_blank ? action :
10244                               i == Ykey_6_blank ? action :
10245                               i == Ykey_7_blank ? action :
10246                               i == Ykey_8_blank ? action :
10247                               i == Ylenses_blank ? action :
10248                               i == Ymagnify_blank ? action :
10249                               i == Ygrass_blank ? action :
10250                               i == Ydirt_blank ? action :
10251                               i == Xsand_stonein_1 ? action :
10252                               i == Xsand_stonein_2 ? action :
10253                               i == Xsand_stonein_3 ? action :
10254                               i == Xsand_stonein_4 ? action :
10255                               i == Xsand_stoneout_1 ? action :
10256                               i == Xsand_stoneout_2 ? action :
10257                               i == Xboom_android ? ACTION_EXPLODING :
10258                               action_exploding ? ACTION_EXPLODING :
10259                               action_active ? action :
10260                               action_other ? action :
10261                               ACTION_DEFAULT);
10262       int graphic = (el_act_dir2img(effective_element, effective_action,
10263                                     direction));
10264       int crumbled = (el_act_dir2crm(effective_element, effective_action,
10265                                      direction));
10266       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10267       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10268       boolean has_action_graphics = (graphic != base_graphic);
10269       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10270       struct GraphicInfo *g = &graphic_info[graphic];
10271       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10272       Bitmap *src_bitmap;
10273       int src_x, src_y;
10274       // ensure to get symmetric 3-frame, 2-delay animations as used in EM
10275       boolean special_animation = (action != ACTION_DEFAULT &&
10276                                    g->anim_frames == 3 &&
10277                                    g->anim_delay == 2 &&
10278                                    g->anim_mode & ANIM_LINEAR);
10279       int sync_frame = (i == Xdrip_stretch ? 7 :
10280                         i == Xdrip_stretchB ? 7 :
10281                         i == Ydrip_2_s ? j + 8 :
10282                         i == Ydrip_2_sB ? j + 8 :
10283                         i == Xacid_1 ? 0 :
10284                         i == Xacid_2 ? 10 :
10285                         i == Xacid_3 ? 20 :
10286                         i == Xacid_4 ? 30 :
10287                         i == Xacid_5 ? 40 :
10288                         i == Xacid_6 ? 50 :
10289                         i == Xacid_7 ? 60 :
10290                         i == Xacid_8 ? 70 :
10291                         i == Xfake_acid_1 ? 0 :
10292                         i == Xfake_acid_2 ? 10 :
10293                         i == Xfake_acid_3 ? 20 :
10294                         i == Xfake_acid_4 ? 30 :
10295                         i == Xfake_acid_5 ? 40 :
10296                         i == Xfake_acid_6 ? 50 :
10297                         i == Xfake_acid_7 ? 60 :
10298                         i == Xfake_acid_8 ? 70 :
10299                         i == Xfake_acid_1_player ? 0 :
10300                         i == Xfake_acid_2_player ? 10 :
10301                         i == Xfake_acid_3_player ? 20 :
10302                         i == Xfake_acid_4_player ? 30 :
10303                         i == Xfake_acid_5_player ? 40 :
10304                         i == Xfake_acid_6_player ? 50 :
10305                         i == Xfake_acid_7_player ? 60 :
10306                         i == Xfake_acid_8_player ? 70 :
10307                         i == Xball_2 ? 7 :
10308                         i == Yball_2 ? j + 8 :
10309                         i == Yball_blank ? j + 1 :
10310                         i == Ykey_1_blank ? j + 1 :
10311                         i == Ykey_2_blank ? j + 1 :
10312                         i == Ykey_3_blank ? j + 1 :
10313                         i == Ykey_4_blank ? j + 1 :
10314                         i == Ykey_5_blank ? j + 1 :
10315                         i == Ykey_6_blank ? j + 1 :
10316                         i == Ykey_7_blank ? j + 1 :
10317                         i == Ykey_8_blank ? j + 1 :
10318                         i == Ylenses_blank ? j + 1 :
10319                         i == Ymagnify_blank ? j + 1 :
10320                         i == Ygrass_blank ? j + 1 :
10321                         i == Ydirt_blank ? j + 1 :
10322                         i == Xamoeba_1 ? 0 :
10323                         i == Xamoeba_2 ? 1 :
10324                         i == Xamoeba_3 ? 2 :
10325                         i == Xamoeba_4 ? 3 :
10326                         i == Xamoeba_5 ? 0 :
10327                         i == Xamoeba_6 ? 1 :
10328                         i == Xamoeba_7 ? 2 :
10329                         i == Xamoeba_8 ? 3 :
10330                         i == Xexit_2 ? j + 8 :
10331                         i == Xexit_3 ? j + 16 :
10332                         i == Xdynamite_1 ? 0 :
10333                         i == Xdynamite_2 ? 8 :
10334                         i == Xdynamite_3 ? 16 :
10335                         i == Xdynamite_4 ? 24 :
10336                         i == Xsand_stonein_1 ? j + 1 :
10337                         i == Xsand_stonein_2 ? j + 9 :
10338                         i == Xsand_stonein_3 ? j + 17 :
10339                         i == Xsand_stonein_4 ? j + 25 :
10340                         i == Xsand_stoneout_1 && j == 0 ? 0 :
10341                         i == Xsand_stoneout_1 && j == 1 ? 0 :
10342                         i == Xsand_stoneout_1 && j == 2 ? 1 :
10343                         i == Xsand_stoneout_1 && j == 3 ? 2 :
10344                         i == Xsand_stoneout_1 && j == 4 ? 2 :
10345                         i == Xsand_stoneout_1 && j == 5 ? 3 :
10346                         i == Xsand_stoneout_1 && j == 6 ? 4 :
10347                         i == Xsand_stoneout_1 && j == 7 ? 4 :
10348                         i == Xsand_stoneout_2 && j == 0 ? 5 :
10349                         i == Xsand_stoneout_2 && j == 1 ? 6 :
10350                         i == Xsand_stoneout_2 && j == 2 ? 7 :
10351                         i == Xsand_stoneout_2 && j == 3 ? 8 :
10352                         i == Xsand_stoneout_2 && j == 4 ? 9 :
10353                         i == Xsand_stoneout_2 && j == 5 ? 11 :
10354                         i == Xsand_stoneout_2 && j == 6 ? 13 :
10355                         i == Xsand_stoneout_2 && j == 7 ? 15 :
10356                         i == Xboom_bug && j == 1 ? 2 :
10357                         i == Xboom_bug && j == 2 ? 2 :
10358                         i == Xboom_bug && j == 3 ? 4 :
10359                         i == Xboom_bug && j == 4 ? 4 :
10360                         i == Xboom_bug && j == 5 ? 2 :
10361                         i == Xboom_bug && j == 6 ? 2 :
10362                         i == Xboom_bug && j == 7 ? 0 :
10363                         i == Xboom_tank && j == 1 ? 2 :
10364                         i == Xboom_tank && j == 2 ? 2 :
10365                         i == Xboom_tank && j == 3 ? 4 :
10366                         i == Xboom_tank && j == 4 ? 4 :
10367                         i == Xboom_tank && j == 5 ? 2 :
10368                         i == Xboom_tank && j == 6 ? 2 :
10369                         i == Xboom_tank && j == 7 ? 0 :
10370                         i == Xboom_android && j == 7 ? 6 :
10371                         i == Xboom_1 && j == 1 ? 2 :
10372                         i == Xboom_1 && j == 2 ? 2 :
10373                         i == Xboom_1 && j == 3 ? 4 :
10374                         i == Xboom_1 && j == 4 ? 4 :
10375                         i == Xboom_1 && j == 5 ? 6 :
10376                         i == Xboom_1 && j == 6 ? 6 :
10377                         i == Xboom_1 && j == 7 ? 8 :
10378                         i == Xboom_2 && j == 0 ? 8 :
10379                         i == Xboom_2 && j == 1 ? 8 :
10380                         i == Xboom_2 && j == 2 ? 10 :
10381                         i == Xboom_2 && j == 3 ? 10 :
10382                         i == Xboom_2 && j == 4 ? 10 :
10383                         i == Xboom_2 && j == 5 ? 12 :
10384                         i == Xboom_2 && j == 6 ? 12 :
10385                         i == Xboom_2 && j == 7 ? 12 :
10386                         special_animation && j == 4 ? 3 :
10387                         effective_action != action ? 0 :
10388                         j);
10389       int frame = getAnimationFrame(g->anim_frames,
10390                                     g->anim_delay,
10391                                     g->anim_mode,
10392                                     g->anim_start_frame,
10393                                     sync_frame);
10394
10395       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
10396                           g->double_movement && is_backside);
10397
10398       g_em->bitmap = src_bitmap;
10399       g_em->src_x = src_x;
10400       g_em->src_y = src_y;
10401       g_em->src_offset_x = 0;
10402       g_em->src_offset_y = 0;
10403       g_em->dst_offset_x = 0;
10404       g_em->dst_offset_y = 0;
10405       g_em->width  = TILEX;
10406       g_em->height = TILEY;
10407
10408       g_em->preserve_background = FALSE;
10409
10410       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10411                                sync_frame);
10412
10413       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
10414                                    effective_action == ACTION_MOVING  ||
10415                                    effective_action == ACTION_PUSHING ||
10416                                    effective_action == ACTION_EATING)) ||
10417           (!has_action_graphics && (effective_action == ACTION_FILLING ||
10418                                     effective_action == ACTION_EMPTYING)))
10419       {
10420         int move_dir =
10421           (effective_action == ACTION_FALLING ||
10422            effective_action == ACTION_FILLING ||
10423            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
10424         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
10425         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
10426         int num_steps = (i == Ydrip_1_s  ? 16 :
10427                          i == Ydrip_1_sB ? 16 :
10428                          i == Ydrip_2_s  ? 16 :
10429                          i == Ydrip_2_sB ? 16 :
10430                          i == Xsand_stonein_1 ? 32 :
10431                          i == Xsand_stonein_2 ? 32 :
10432                          i == Xsand_stonein_3 ? 32 :
10433                          i == Xsand_stonein_4 ? 32 :
10434                          i == Xsand_stoneout_1 ? 16 :
10435                          i == Xsand_stoneout_2 ? 16 : 8);
10436         int cx = ABS(dx) * (TILEX / num_steps);
10437         int cy = ABS(dy) * (TILEY / num_steps);
10438         int step_frame = (i == Ydrip_2_s        ? j + 8 :
10439                           i == Ydrip_2_sB       ? j + 8 :
10440                           i == Xsand_stonein_2  ? j + 8 :
10441                           i == Xsand_stonein_3  ? j + 16 :
10442                           i == Xsand_stonein_4  ? j + 24 :
10443                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
10444         int step = (is_backside ? step_frame : num_steps - step_frame);
10445
10446         if (is_backside)        // tile where movement starts
10447         {
10448           if (dx < 0 || dy < 0)
10449           {
10450             g_em->src_offset_x = cx * step;
10451             g_em->src_offset_y = cy * step;
10452           }
10453           else
10454           {
10455             g_em->dst_offset_x = cx * step;
10456             g_em->dst_offset_y = cy * step;
10457           }
10458         }
10459         else                    // tile where movement ends
10460         {
10461           if (dx < 0 || dy < 0)
10462           {
10463             g_em->dst_offset_x = cx * step;
10464             g_em->dst_offset_y = cy * step;
10465           }
10466           else
10467           {
10468             g_em->src_offset_x = cx * step;
10469             g_em->src_offset_y = cy * step;
10470           }
10471         }
10472
10473         g_em->width  = TILEX - cx * step;
10474         g_em->height = TILEY - cy * step;
10475       }
10476
10477       // create unique graphic identifier to decide if tile must be redrawn
10478       /* bit 31 - 16 (16 bit): EM style graphic
10479          bit 15 - 12 ( 4 bit): EM style frame
10480          bit 11 -  6 ( 6 bit): graphic width
10481          bit  5 -  0 ( 6 bit): graphic height */
10482       g_em->unique_identifier =
10483         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
10484     }
10485   }
10486
10487   for (i = 0; i < GAME_TILE_MAX; i++)
10488   {
10489     for (j = 0; j < 8; j++)
10490     {
10491       int element = em_object_mapping[i].element_rnd;
10492       int action = em_object_mapping[i].action;
10493       int direction = em_object_mapping[i].direction;
10494       boolean is_backside = em_object_mapping[i].is_backside;
10495       int graphic_action  = el_act_dir2img(element, action, direction);
10496       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
10497
10498       if ((action == ACTION_SMASHED_BY_ROCK ||
10499            action == ACTION_SMASHED_BY_SPRING ||
10500            action == ACTION_EATING) &&
10501           graphic_action == graphic_default)
10502       {
10503         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
10504                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
10505                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
10506                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
10507                  Xspring);
10508
10509         // no separate animation for "smashed by rock" -- use rock instead
10510         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10511         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
10512
10513         g_em->bitmap            = g_xx->bitmap;
10514         g_em->src_x             = g_xx->src_x;
10515         g_em->src_y             = g_xx->src_y;
10516         g_em->src_offset_x      = g_xx->src_offset_x;
10517         g_em->src_offset_y      = g_xx->src_offset_y;
10518         g_em->dst_offset_x      = g_xx->dst_offset_x;
10519         g_em->dst_offset_y      = g_xx->dst_offset_y;
10520         g_em->width             = g_xx->width;
10521         g_em->height            = g_xx->height;
10522         g_em->unique_identifier = g_xx->unique_identifier;
10523
10524         if (!is_backside)
10525           g_em->preserve_background = TRUE;
10526       }
10527     }
10528   }
10529
10530   for (p = 0; p < MAX_PLAYERS; p++)
10531   {
10532     for (i = 0; i < PLY_MAX; i++)
10533     {
10534       int element = em_player_mapping[p][i].element_rnd;
10535       int action = em_player_mapping[p][i].action;
10536       int direction = em_player_mapping[p][i].direction;
10537
10538       for (j = 0; j < 8; j++)
10539       {
10540         int effective_element = element;
10541         int effective_action = action;
10542         int graphic = (direction == MV_NONE ?
10543                        el_act2img(effective_element, effective_action) :
10544                        el_act_dir2img(effective_element, effective_action,
10545                                       direction));
10546         struct GraphicInfo *g = &graphic_info[graphic];
10547         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
10548         Bitmap *src_bitmap;
10549         int src_x, src_y;
10550         int sync_frame = j;
10551         int frame = getAnimationFrame(g->anim_frames,
10552                                       g->anim_delay,
10553                                       g->anim_mode,
10554                                       g->anim_start_frame,
10555                                       sync_frame);
10556
10557         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10558
10559         g_em->bitmap = src_bitmap;
10560         g_em->src_x = src_x;
10561         g_em->src_y = src_y;
10562         g_em->src_offset_x = 0;
10563         g_em->src_offset_y = 0;
10564         g_em->dst_offset_x = 0;
10565         g_em->dst_offset_y = 0;
10566         g_em->width  = TILEX;
10567         g_em->height = TILEY;
10568       }
10569     }
10570   }
10571 }
10572
10573 static void CheckSaveEngineSnapshot_EM(int frame,
10574                                        boolean any_player_moving,
10575                                        boolean any_player_snapping,
10576                                        boolean any_player_dropping)
10577 {
10578   if (frame == 7 && !any_player_dropping)
10579   {
10580     if (!local_player->was_waiting)
10581     {
10582       if (!CheckSaveEngineSnapshotToList())
10583         return;
10584
10585       local_player->was_waiting = TRUE;
10586     }
10587   }
10588   else if (any_player_moving || any_player_snapping || any_player_dropping)
10589   {
10590     local_player->was_waiting = FALSE;
10591   }
10592 }
10593
10594 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
10595                                        boolean murphy_is_dropping)
10596 {
10597   if (murphy_is_waiting)
10598   {
10599     if (!local_player->was_waiting)
10600     {
10601       if (!CheckSaveEngineSnapshotToList())
10602         return;
10603
10604       local_player->was_waiting = TRUE;
10605     }
10606   }
10607   else
10608   {
10609     local_player->was_waiting = FALSE;
10610   }
10611 }
10612
10613 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
10614                                        boolean button_released)
10615 {
10616   if (button_released)
10617   {
10618     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
10619       CheckSaveEngineSnapshotToList();
10620   }
10621   else if (element_clicked)
10622   {
10623     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
10624       CheckSaveEngineSnapshotToList();
10625
10626     game.snapshot.changed_action = TRUE;
10627   }
10628 }
10629
10630 boolean CheckSingleStepMode_EM(int frame,
10631                                boolean any_player_moving,
10632                                boolean any_player_snapping,
10633                                boolean any_player_dropping)
10634 {
10635   if (tape.single_step && tape.recording && !tape.pausing)
10636     if (frame == 7 && !any_player_dropping && FrameCounter > 6)
10637       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10638
10639   CheckSaveEngineSnapshot_EM(frame, any_player_moving,
10640                              any_player_snapping, any_player_dropping);
10641
10642   return tape.pausing;
10643 }
10644
10645 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
10646                             boolean murphy_is_dropping)
10647 {
10648   boolean murphy_starts_dropping = FALSE;
10649   int i;
10650
10651   for (i = 0; i < MAX_PLAYERS; i++)
10652     if (stored_player[i].force_dropping)
10653       murphy_starts_dropping = TRUE;
10654
10655   if (tape.single_step && tape.recording && !tape.pausing)
10656     if (murphy_is_waiting && !murphy_starts_dropping)
10657       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10658
10659   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
10660 }
10661
10662 void CheckSingleStepMode_MM(boolean element_clicked,
10663                             boolean button_released)
10664 {
10665   if (tape.single_step && tape.recording && !tape.pausing)
10666     if (button_released)
10667       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10668
10669   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
10670 }
10671
10672 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
10673                          int graphic, int sync_frame)
10674 {
10675   int frame = getGraphicAnimationFrame(graphic, sync_frame);
10676
10677   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
10678 }
10679
10680 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
10681 {
10682   return (IS_NEXT_FRAME(sync_frame, graphic));
10683 }
10684
10685 int getGraphicInfo_Delay(int graphic)
10686 {
10687   return graphic_info[graphic].anim_delay;
10688 }
10689
10690 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
10691 {
10692   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
10693     return FALSE;
10694
10695   if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
10696     return FALSE;
10697
10698   return TRUE;
10699 }
10700
10701 void PlayMenuSoundExt(int sound)
10702 {
10703   if (sound == SND_UNDEFINED)
10704     return;
10705
10706   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10707       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10708     return;
10709
10710   if (IS_LOOP_SOUND(sound))
10711     PlaySoundLoop(sound);
10712   else
10713     PlaySound(sound);
10714 }
10715
10716 void PlayMenuSound(void)
10717 {
10718   PlayMenuSoundExt(menu.sound[game_status]);
10719 }
10720
10721 void PlayMenuSoundStereo(int sound, int stereo_position)
10722 {
10723   if (sound == SND_UNDEFINED)
10724     return;
10725
10726   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10727       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10728     return;
10729
10730   if (IS_LOOP_SOUND(sound))
10731     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
10732   else
10733     PlaySoundStereo(sound, stereo_position);
10734 }
10735
10736 void PlayMenuSoundIfLoopExt(int sound)
10737 {
10738   if (sound == SND_UNDEFINED)
10739     return;
10740
10741   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10742       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10743     return;
10744
10745   if (IS_LOOP_SOUND(sound))
10746     PlaySoundLoop(sound);
10747 }
10748
10749 void PlayMenuSoundIfLoop(void)
10750 {
10751   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
10752 }
10753
10754 void PlayMenuMusicExt(int music)
10755 {
10756   if (music == MUS_UNDEFINED)
10757     return;
10758
10759   if (!setup.sound_music)
10760     return;
10761
10762   if (IS_LOOP_MUSIC(music))
10763     PlayMusicLoop(music);
10764   else
10765     PlayMusic(music);
10766 }
10767
10768 void PlayMenuMusic(void)
10769 {
10770   char *curr_music = getCurrentlyPlayingMusicFilename();
10771   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
10772
10773   if (!strEqual(curr_music, next_music))
10774     PlayMenuMusicExt(menu.music[game_status]);
10775 }
10776
10777 void PlayMenuSoundsAndMusic(void)
10778 {
10779   PlayMenuSound();
10780   PlayMenuMusic();
10781 }
10782
10783 static void FadeMenuSounds(void)
10784 {
10785   FadeSounds();
10786 }
10787
10788 static void FadeMenuMusic(void)
10789 {
10790   char *curr_music = getCurrentlyPlayingMusicFilename();
10791   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
10792
10793   if (!strEqual(curr_music, next_music))
10794     FadeMusic();
10795 }
10796
10797 void FadeMenuSoundsAndMusic(void)
10798 {
10799   FadeMenuSounds();
10800   FadeMenuMusic();
10801 }
10802
10803 void PlaySoundActivating(void)
10804 {
10805 #if 0
10806   PlaySound(SND_MENU_ITEM_ACTIVATING);
10807 #endif
10808 }
10809
10810 void PlaySoundSelecting(void)
10811 {
10812 #if 0
10813   PlaySound(SND_MENU_ITEM_SELECTING);
10814 #endif
10815 }
10816
10817 void ToggleFullscreenIfNeeded(void)
10818 {
10819   // if setup and video fullscreen state are already matching, nothing do do
10820   if (setup.fullscreen == video.fullscreen_enabled ||
10821       !video.fullscreen_available)
10822     return;
10823
10824   SDLSetWindowFullscreen(setup.fullscreen);
10825
10826   // set setup value according to successfully changed fullscreen mode
10827   setup.fullscreen = video.fullscreen_enabled;
10828 }
10829
10830 void ChangeWindowScalingIfNeeded(void)
10831 {
10832   // if setup and video window scaling are already matching, nothing do do
10833   if (setup.window_scaling_percent == video.window_scaling_percent ||
10834       video.fullscreen_enabled)
10835     return;
10836
10837   SDLSetWindowScaling(setup.window_scaling_percent);
10838
10839   // set setup value according to successfully changed window scaling
10840   setup.window_scaling_percent = video.window_scaling_percent;
10841 }
10842
10843 void ChangeVsyncModeIfNeeded(void)
10844 {
10845   int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
10846   int video_vsync_mode = video.vsync_mode;
10847
10848   // if setup and video vsync mode are already matching, nothing do do
10849   if (setup_vsync_mode == video_vsync_mode)
10850     return;
10851
10852   // if renderer is using OpenGL, vsync mode can directly be changed
10853   SDLSetScreenVsyncMode(setup.vsync_mode);
10854
10855   // if vsync mode unchanged, try re-creating renderer to set vsync mode
10856   if (video.vsync_mode == video_vsync_mode)
10857   {
10858     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
10859
10860     // save backbuffer content which gets lost when re-creating screen
10861     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10862
10863     // force re-creating screen and renderer to set new vsync mode
10864     video.fullscreen_enabled = !setup.fullscreen;
10865
10866     // when creating new renderer, destroy textures linked to old renderer
10867     FreeAllImageTextures();     // needs old renderer to free the textures
10868
10869     // re-create screen and renderer (including change of vsync mode)
10870     ChangeVideoModeIfNeeded(setup.fullscreen);
10871
10872     // set setup value according to successfully changed fullscreen mode
10873     setup.fullscreen = video.fullscreen_enabled;
10874
10875     // restore backbuffer content from temporary backbuffer backup bitmap
10876     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10877     FreeBitmap(tmp_backbuffer);
10878
10879     // update visible window/screen
10880     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10881
10882     // when changing vsync mode, re-create textures for new renderer
10883     InitImageTextures();
10884   }
10885
10886   // set setup value according to successfully changed vsync mode
10887   setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
10888 }
10889
10890 static void JoinRectangles(int *x, int *y, int *width, int *height,
10891                            int x2, int y2, int width2, int height2)
10892 {
10893   // do not join with "off-screen" rectangle
10894   if (x2 == -1 || y2 == -1)
10895     return;
10896
10897   *x = MIN(*x, x2);
10898   *y = MIN(*y, y2);
10899   *width = MAX(*width, width2);
10900   *height = MAX(*height, height2);
10901 }
10902
10903 void SetAnimStatus(int anim_status_new)
10904 {
10905   if (anim_status_new == GAME_MODE_MAIN)
10906     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
10907   else if (anim_status_new == GAME_MODE_NAMES)
10908     anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
10909   else if (anim_status_new == GAME_MODE_SCORES)
10910     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
10911
10912   global.anim_status_next = anim_status_new;
10913
10914   // directly set screen modes that are entered without fading
10915   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
10916        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
10917       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
10918        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
10919       (global.anim_status      == GAME_MODE_PSEUDO_NAMESONLY &&
10920        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
10921       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAMES &&
10922        global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
10923     global.anim_status = global.anim_status_next;
10924 }
10925
10926 void SetGameStatus(int game_status_new)
10927 {
10928   if (game_status_new != game_status)
10929     game_status_last_screen = game_status;
10930
10931   game_status = game_status_new;
10932
10933   SetAnimStatus(game_status_new);
10934 }
10935
10936 void SetFontStatus(int game_status_new)
10937 {
10938   static int last_game_status = -1;
10939
10940   if (game_status_new != -1)
10941   {
10942     // set game status for font use after storing last game status
10943     last_game_status = game_status;
10944     game_status = game_status_new;
10945   }
10946   else
10947   {
10948     // reset game status after font use from last stored game status
10949     game_status = last_game_status;
10950   }
10951 }
10952
10953 void ResetFontStatus(void)
10954 {
10955   SetFontStatus(-1);
10956 }
10957
10958 void SetLevelSetInfo(char *identifier, int level_nr)
10959 {
10960   setString(&levelset.identifier, identifier);
10961
10962   levelset.level_nr = level_nr;
10963 }
10964
10965 boolean CheckIfAllViewportsHaveChanged(void)
10966 {
10967   // if game status has not changed, viewports have not changed either
10968   if (game_status == game_status_last)
10969     return FALSE;
10970
10971   // check if all viewports have changed with current game status
10972
10973   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
10974   struct RectWithBorder *vp_door_1    = &viewport.door_1[game_status];
10975   struct RectWithBorder *vp_door_2    = &viewport.door_2[game_status];
10976   int new_real_sx       = vp_playfield->x;
10977   int new_real_sy       = vp_playfield->y;
10978   int new_full_sxsize   = vp_playfield->width;
10979   int new_full_sysize   = vp_playfield->height;
10980   int new_dx            = vp_door_1->x;
10981   int new_dy            = vp_door_1->y;
10982   int new_dxsize        = vp_door_1->width;
10983   int new_dysize        = vp_door_1->height;
10984   int new_vx            = vp_door_2->x;
10985   int new_vy            = vp_door_2->y;
10986   int new_vxsize        = vp_door_2->width;
10987   int new_vysize        = vp_door_2->height;
10988
10989   boolean playfield_viewport_has_changed =
10990     (new_real_sx != REAL_SX ||
10991      new_real_sy != REAL_SY ||
10992      new_full_sxsize != FULL_SXSIZE ||
10993      new_full_sysize != FULL_SYSIZE);
10994
10995   boolean door_1_viewport_has_changed =
10996     (new_dx != DX ||
10997      new_dy != DY ||
10998      new_dxsize != DXSIZE ||
10999      new_dysize != DYSIZE);
11000
11001   boolean door_2_viewport_has_changed =
11002     (new_vx != VX ||
11003      new_vy != VY ||
11004      new_vxsize != VXSIZE ||
11005      new_vysize != VYSIZE ||
11006      game_status_last == GAME_MODE_EDITOR);
11007
11008   return (playfield_viewport_has_changed &&
11009           door_1_viewport_has_changed &&
11010           door_2_viewport_has_changed);
11011 }
11012
11013 boolean CheckFadeAll(void)
11014 {
11015   return (CheckIfGlobalBorderHasChanged() ||
11016           CheckIfAllViewportsHaveChanged());
11017 }
11018
11019 void ChangeViewportPropertiesIfNeeded(void)
11020 {
11021   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
11022                                FALSE : setup.small_game_graphics);
11023   int gfx_game_mode = getGlobalGameStatus(game_status);
11024   int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
11025                         gfx_game_mode);
11026   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
11027   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
11028   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
11029   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
11030   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
11031   int new_win_xsize     = vp_window->width;
11032   int new_win_ysize     = vp_window->height;
11033   int border_left       = vp_playfield->border_left;
11034   int border_right      = vp_playfield->border_right;
11035   int border_top        = vp_playfield->border_top;
11036   int border_bottom     = vp_playfield->border_bottom;
11037   int new_sx            = vp_playfield->x      + border_left;
11038   int new_sy            = vp_playfield->y      + border_top;
11039   int new_sxsize        = vp_playfield->width  - border_left - border_right;
11040   int new_sysize        = vp_playfield->height - border_top  - border_bottom;
11041   int new_real_sx       = vp_playfield->x;
11042   int new_real_sy       = vp_playfield->y;
11043   int new_full_sxsize   = vp_playfield->width;
11044   int new_full_sysize   = vp_playfield->height;
11045   int new_dx            = vp_door_1->x;
11046   int new_dy            = vp_door_1->y;
11047   int new_dxsize        = vp_door_1->width;
11048   int new_dysize        = vp_door_1->height;
11049   int new_vx            = vp_door_2->x;
11050   int new_vy            = vp_door_2->y;
11051   int new_vxsize        = vp_door_2->width;
11052   int new_vysize        = vp_door_2->height;
11053   int new_ex            = vp_door_3->x;
11054   int new_ey            = vp_door_3->y;
11055   int new_exsize        = vp_door_3->width;
11056   int new_eysize        = vp_door_3->height;
11057   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
11058   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
11059                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
11060   int new_scr_fieldx = new_sxsize / tilesize;
11061   int new_scr_fieldy = new_sysize / tilesize;
11062   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
11063   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
11064   boolean init_gfx_buffers = FALSE;
11065   boolean init_video_buffer = FALSE;
11066   boolean init_gadgets_and_anims = FALSE;
11067   boolean init_bd_graphics = FALSE;
11068   boolean init_em_graphics = FALSE;
11069
11070   if (new_win_xsize != WIN_XSIZE ||
11071       new_win_ysize != WIN_YSIZE)
11072   {
11073     WIN_XSIZE = new_win_xsize;
11074     WIN_YSIZE = new_win_ysize;
11075
11076     init_video_buffer = TRUE;
11077     init_gfx_buffers = TRUE;
11078     init_gadgets_and_anims = TRUE;
11079
11080     // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
11081   }
11082
11083   if (new_scr_fieldx != SCR_FIELDX ||
11084       new_scr_fieldy != SCR_FIELDY)
11085   {
11086     // this always toggles between MAIN and GAME when using small tile size
11087
11088     SCR_FIELDX = new_scr_fieldx;
11089     SCR_FIELDY = new_scr_fieldy;
11090
11091     // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
11092   }
11093
11094   if (new_sx != SX ||
11095       new_sy != SY ||
11096       new_dx != DX ||
11097       new_dy != DY ||
11098       new_vx != VX ||
11099       new_vy != VY ||
11100       new_ex != EX ||
11101       new_ey != EY ||
11102       new_sxsize != SXSIZE ||
11103       new_sysize != SYSIZE ||
11104       new_dxsize != DXSIZE ||
11105       new_dysize != DYSIZE ||
11106       new_vxsize != VXSIZE ||
11107       new_vysize != VYSIZE ||
11108       new_exsize != EXSIZE ||
11109       new_eysize != EYSIZE ||
11110       new_real_sx != REAL_SX ||
11111       new_real_sy != REAL_SY ||
11112       new_full_sxsize != FULL_SXSIZE ||
11113       new_full_sysize != FULL_SYSIZE ||
11114       new_tilesize_var != TILESIZE_VAR
11115       )
11116   {
11117     // ------------------------------------------------------------------------
11118     // determine next fading area for changed viewport definitions
11119     // ------------------------------------------------------------------------
11120
11121     // start with current playfield area (default fading area)
11122     FADE_SX = REAL_SX;
11123     FADE_SY = REAL_SY;
11124     FADE_SXSIZE = FULL_SXSIZE;
11125     FADE_SYSIZE = FULL_SYSIZE;
11126
11127     // add new playfield area if position or size has changed
11128     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
11129         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
11130     {
11131       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11132                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
11133     }
11134
11135     // add current and new door 1 area if position or size has changed
11136     if (new_dx != DX || new_dy != DY ||
11137         new_dxsize != DXSIZE || new_dysize != DYSIZE)
11138     {
11139       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11140                      DX, DY, DXSIZE, DYSIZE);
11141       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11142                      new_dx, new_dy, new_dxsize, new_dysize);
11143     }
11144
11145     // add current and new door 2 area if position or size has changed
11146     if (new_vx != VX || new_vy != VY ||
11147         new_vxsize != VXSIZE || new_vysize != VYSIZE)
11148     {
11149       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11150                      VX, VY, VXSIZE, VYSIZE);
11151       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11152                      new_vx, new_vy, new_vxsize, new_vysize);
11153     }
11154
11155     // ------------------------------------------------------------------------
11156     // handle changed tile size
11157     // ------------------------------------------------------------------------
11158
11159     if (new_tilesize_var != TILESIZE_VAR)
11160     {
11161       // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
11162
11163       // changing tile size invalidates scroll values of engine snapshots
11164       FreeEngineSnapshotSingle();
11165
11166       // changing tile size requires update of graphic mapping for BD/EM engine
11167       init_bd_graphics = TRUE;
11168       init_em_graphics = TRUE;
11169     }
11170
11171     SX = new_sx;
11172     SY = new_sy;
11173     DX = new_dx;
11174     DY = new_dy;
11175     VX = new_vx;
11176     VY = new_vy;
11177     EX = new_ex;
11178     EY = new_ey;
11179     SXSIZE = new_sxsize;
11180     SYSIZE = new_sysize;
11181     DXSIZE = new_dxsize;
11182     DYSIZE = new_dysize;
11183     VXSIZE = new_vxsize;
11184     VYSIZE = new_vysize;
11185     EXSIZE = new_exsize;
11186     EYSIZE = new_eysize;
11187     REAL_SX = new_real_sx;
11188     REAL_SY = new_real_sy;
11189     FULL_SXSIZE = new_full_sxsize;
11190     FULL_SYSIZE = new_full_sysize;
11191     TILESIZE_VAR = new_tilesize_var;
11192
11193     init_gfx_buffers = TRUE;
11194     init_gadgets_and_anims = TRUE;
11195
11196     // Debug("tools:viewport", "viewports: init_gfx_buffers");
11197     // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
11198   }
11199
11200   if (init_gfx_buffers)
11201   {
11202     // Debug("tools:viewport", "init_gfx_buffers");
11203
11204     SCR_FIELDX = new_scr_fieldx_buffers;
11205     SCR_FIELDY = new_scr_fieldy_buffers;
11206
11207     InitGfxBuffers();
11208
11209     SCR_FIELDX = new_scr_fieldx;
11210     SCR_FIELDY = new_scr_fieldy;
11211
11212     SetDrawDeactivationMask(REDRAW_NONE);
11213     SetDrawBackgroundMask(REDRAW_FIELD);
11214   }
11215
11216   if (init_video_buffer)
11217   {
11218     // Debug("tools:viewport", "init_video_buffer");
11219
11220     FreeAllImageTextures();     // needs old renderer to free the textures
11221
11222     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
11223     InitImageTextures();
11224   }
11225
11226   if (init_gadgets_and_anims)
11227   {
11228     // Debug("tools:viewport", "init_gadgets_and_anims");
11229
11230     InitGadgets();
11231     InitGlobalAnimations();
11232   }
11233
11234   if (init_bd_graphics)
11235   {
11236     InitGraphicInfo_BD();
11237   }
11238
11239   if (init_em_graphics)
11240   {
11241     InitGraphicInfo_EM();
11242   }
11243 }
11244
11245 void OpenURL(char *url)
11246 {
11247 #if SDL_VERSION_ATLEAST(2,0,14)
11248   SDL_OpenURL(url);
11249 #else
11250   Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
11251        url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
11252   Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
11253 #endif
11254 }
11255
11256 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
11257 {
11258   OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
11259 }
11260
11261 char *getCurrentLevelsetName(void)
11262 {
11263   return leveldir_current->name;
11264 }
11265
11266
11267 // ============================================================================
11268 // tests
11269 // ============================================================================
11270
11271 #if defined(PLATFORM_WINDOWS)
11272 /* FILETIME of Jan 1 1970 00:00:00. */
11273 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
11274
11275 /*
11276  * timezone information is stored outside the kernel so tzp isn't used anymore.
11277  *
11278  * Note: this function is not for Win32 high precision timing purpose. See
11279  * elapsed_time().
11280  */
11281 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
11282 {
11283   FILETIME    file_time;
11284   SYSTEMTIME  system_time;
11285   ULARGE_INTEGER ularge;
11286
11287   GetSystemTime(&system_time);
11288   SystemTimeToFileTime(&system_time, &file_time);
11289   ularge.LowPart = file_time.dwLowDateTime;
11290   ularge.HighPart = file_time.dwHighDateTime;
11291
11292   tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
11293   tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
11294
11295   return 0;
11296 }
11297 #endif
11298
11299 static char *test_init_uuid_random_function_simple(void)
11300 {
11301   static char seed_text[100];
11302   unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
11303
11304   sprintf(seed_text, "%d", seed);
11305
11306   return seed_text;
11307 }
11308
11309 static char *test_init_uuid_random_function_better(void)
11310 {
11311   static char seed_text[100];
11312   struct timeval current_time;
11313
11314   gettimeofday(&current_time, NULL);
11315
11316   prng_seed_bytes(&current_time, sizeof(current_time));
11317
11318   sprintf(seed_text, "%ld.%ld",
11319           (long)current_time.tv_sec,
11320           (long)current_time.tv_usec);
11321
11322   return seed_text;
11323 }
11324
11325 #if defined(PLATFORM_WINDOWS)
11326 static char *test_init_uuid_random_function_better_windows(void)
11327 {
11328   static char seed_text[100];
11329   struct timeval current_time;
11330
11331   gettimeofday_windows(&current_time, NULL);
11332
11333   prng_seed_bytes(&current_time, sizeof(current_time));
11334
11335   sprintf(seed_text, "%ld.%ld",
11336           (long)current_time.tv_sec,
11337           (long)current_time.tv_usec);
11338
11339   return seed_text;
11340 }
11341 #endif
11342
11343 static unsigned int test_uuid_random_function_simple(int max)
11344 {
11345   return GetSimpleRandom(max);
11346 }
11347
11348 static unsigned int test_uuid_random_function_better(int max)
11349 {
11350   return (max > 0 ? prng_get_uint() % max : 0);
11351 }
11352
11353 #if defined(PLATFORM_WINDOWS)
11354 #define NUM_UUID_TESTS                  3
11355 #else
11356 #define NUM_UUID_TESTS                  2
11357 #endif
11358
11359 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
11360 {
11361   HashTable *hash_seeds =
11362     create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
11363   HashTable *hash_uuids =
11364     create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
11365   static char message[100];
11366   int i;
11367
11368   char *random_name = (nr == 0 ? "simple" : "better");
11369   char *random_type = (always_seed ? "always" : "only once");
11370   char *(*init_random_function)(void) =
11371     (nr == 0 ?
11372      test_init_uuid_random_function_simple :
11373      test_init_uuid_random_function_better);
11374   unsigned int (*random_function)(int) =
11375     (nr == 0 ?
11376      test_uuid_random_function_simple :
11377      test_uuid_random_function_better);
11378   int xpos = 40;
11379
11380 #if defined(PLATFORM_WINDOWS)
11381   if (nr == 2)
11382   {
11383     random_name = "windows";
11384     init_random_function = test_init_uuid_random_function_better_windows;
11385   }
11386 #endif
11387
11388   ClearField();
11389
11390   DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
11391   DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
11392
11393   DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
11394   DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
11395   DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
11396
11397   DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
11398
11399   BackToFront();
11400
11401   // always initialize random number generator at least once
11402   init_random_function();
11403
11404   unsigned int time_start = SDL_GetTicks();
11405
11406   for (i = 0; i < num_uuids; i++)
11407   {
11408     if (always_seed)
11409     {
11410       char *seed = getStringCopy(init_random_function());
11411
11412       hashtable_remove(hash_seeds, seed);
11413       hashtable_insert(hash_seeds, seed, "1");
11414     }
11415
11416     char *uuid = getStringCopy(getUUIDExt(random_function));
11417
11418     hashtable_remove(hash_uuids, uuid);
11419     hashtable_insert(hash_uuids, uuid, "1");
11420   }
11421
11422   int num_unique_seeds = hashtable_count(hash_seeds);
11423   int num_unique_uuids = hashtable_count(hash_uuids);
11424
11425   unsigned int time_needed = SDL_GetTicks() - time_start;
11426
11427   DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
11428
11429   DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
11430
11431   if (always_seed)
11432     DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
11433
11434   if (nr == NUM_UUID_TESTS - 1 && always_seed)
11435     DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
11436   else
11437     DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
11438
11439   sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
11440
11441   Request(message, REQ_CONFIRM);
11442
11443   hashtable_destroy(hash_seeds);
11444   hashtable_destroy(hash_uuids);
11445 }
11446
11447 void TestGeneratingUUIDs(void)
11448 {
11449   int num_uuids = 1000000;
11450   int i, j;
11451
11452   for (i = 0; i < NUM_UUID_TESTS; i++)
11453     for (j = 0; j < 2; j++)
11454       TestGeneratingUUIDs_RunTest(i, j, num_uuids);
11455
11456   CloseAllAndExit(0);
11457 }