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