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