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