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