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