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