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