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