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