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