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