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