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