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