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