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