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