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