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