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