fixed bug which could cause division by zero
[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 DrawSizedElement(int x, int y, int element, int tilesize)
2032 {
2033   int graphic;
2034
2035   graphic = el2edimg(element);
2036   DrawSizedGraphic(x, y, graphic, 0, tilesize);
2037 }
2038
2039 void DrawMiniElement(int x, int y, int element)
2040 {
2041   int graphic;
2042
2043   graphic = el2edimg(element);
2044   DrawMiniGraphic(x, y, graphic);
2045 }
2046
2047 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2048                             int tilesize)
2049 {
2050   int x = sx + scroll_x, y = sy + scroll_y;
2051
2052   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2053     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2054   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2055     DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2056   else
2057     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2058 }
2059
2060 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2061 {
2062   int x = sx + scroll_x, y = sy + scroll_y;
2063
2064   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2065     DrawMiniElement(sx, sy, EL_EMPTY);
2066   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2067     DrawMiniElement(sx, sy, Feld[x][y]);
2068   else
2069     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2070 }
2071
2072 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2073                                  int x, int y, int xsize, int ysize,
2074                                  int tile_width, int tile_height)
2075 {
2076   Bitmap *src_bitmap;
2077   int src_x, src_y;
2078   int dst_x = startx + x * tile_width;
2079   int dst_y = starty + y * tile_height;
2080   int width  = graphic_info[graphic].width;
2081   int height = graphic_info[graphic].height;
2082   int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
2083   int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2084   int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
2085   int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2086   int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
2087   int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2088   boolean draw_masked = graphic_info[graphic].draw_masked;
2089
2090   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2091
2092   if (src_bitmap == NULL || width < tile_width || height < tile_height)
2093   {
2094     ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2095     return;
2096   }
2097
2098   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
2099             inner_sx + (x - 1) * tile_width  % inner_width);
2100   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2101             inner_sy + (y - 1) * tile_height % inner_height);
2102
2103   if (draw_masked)
2104     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2105                      dst_x, dst_y);
2106   else
2107     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2108                dst_x, dst_y);
2109 }
2110
2111 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2112                             int x, int y, int xsize, int ysize, int font_nr)
2113 {
2114   int font_width  = getFontWidth(font_nr);
2115   int font_height = getFontHeight(font_nr);
2116
2117   DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2118                               font_width, font_height);
2119 }
2120
2121 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2122 {
2123   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2124   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2125   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2126   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2127   boolean no_delay = (tape.warp_forward);
2128   unsigned int anim_delay = 0;
2129   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2130   int anim_delay_value = (no_delay ? 0 : frame_delay_value);
2131   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2132   int font_width = getFontWidth(font_nr);
2133   int font_height = getFontHeight(font_nr);
2134   int max_xsize = level.envelope[envelope_nr].xsize;
2135   int max_ysize = level.envelope[envelope_nr].ysize;
2136   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2137   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2138   int xend = max_xsize;
2139   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2140   int xstep = (xstart < xend ? 1 : 0);
2141   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2142   int x, y;
2143
2144   for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2145   {
2146     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2147     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2148     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2149     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2150     int xx, yy;
2151
2152     SetDrawtoField(DRAW_BUFFERED);
2153
2154     BlitScreenToBitmap(backbuffer);
2155
2156     SetDrawtoField(DRAW_BACKBUFFER);
2157
2158     for (yy = 0; yy < ysize; yy++)
2159       for (xx = 0; xx < xsize; xx++)
2160         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2161
2162     DrawTextBuffer(sx + font_width, sy + font_height,
2163                    level.envelope[envelope_nr].text, font_nr, max_xsize,
2164                    xsize - 2, ysize - 2, 0, mask_mode,
2165                    level.envelope[envelope_nr].autowrap,
2166                    level.envelope[envelope_nr].centered, FALSE);
2167
2168     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2169     BackToFront();
2170
2171     WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2172   }
2173 }
2174
2175 void ShowEnvelope(int envelope_nr)
2176 {
2177   int element = EL_ENVELOPE_1 + envelope_nr;
2178   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2179   int sound_opening = element_info[element].sound[ACTION_OPENING];
2180   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2181   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2182   boolean no_delay = (tape.warp_forward);
2183   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2184   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2185   int anim_mode = graphic_info[graphic].anim_mode;
2186   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2187                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2188
2189   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2190
2191   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2192
2193   if (anim_mode == ANIM_DEFAULT)
2194     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2195
2196   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2197
2198   if (tape.playing)
2199     Delay(wait_delay_value);
2200   else
2201     WaitForEventToContinue();
2202
2203   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2204
2205   if (anim_mode != ANIM_NONE)
2206     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2207
2208   if (anim_mode == ANIM_DEFAULT)
2209     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2210
2211   game.envelope_active = FALSE;
2212
2213   SetDrawtoField(DRAW_BUFFERED);
2214
2215   redraw_mask |= REDRAW_FIELD;
2216   BackToFront();
2217 }
2218
2219 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2220 {
2221   int border_size = request.border_size;
2222   int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2223   int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2224   int sx = sx_center - request.width  / 2;
2225   int sy = sy_center - request.height / 2;
2226
2227   if (add_border_size)
2228   {
2229     sx += border_size;
2230     sy += border_size;
2231   }
2232
2233   *x = sx;
2234   *y = sy;
2235 }
2236
2237 void DrawEnvelopeRequest(char *text)
2238 {
2239   char *text_final = text;
2240   char *text_door_style = NULL;
2241   int graphic = IMG_BACKGROUND_REQUEST;
2242   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2243   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2244   int font_nr = FONT_REQUEST;
2245   int font_width = getFontWidth(font_nr);
2246   int font_height = getFontHeight(font_nr);
2247   int border_size = request.border_size;
2248   int line_spacing = request.line_spacing;
2249   int line_height = font_height + line_spacing;
2250   int text_width = request.width - 2 * border_size;
2251   int text_height = request.height - 2 * border_size;
2252   int line_length = text_width / font_width;
2253   int max_lines = text_height / line_height;
2254   int width = request.width;
2255   int height = request.height;
2256   int tile_size = request.step_offset;
2257   int x_steps = width  / tile_size;
2258   int y_steps = height / tile_size;
2259   int sx, sy;
2260   int i, x, y;
2261
2262   if (request.wrap_single_words)
2263   {
2264     char *src_text_ptr, *dst_text_ptr;
2265
2266     text_door_style = checked_malloc(2 * strlen(text) + 1);
2267
2268     src_text_ptr = text;
2269     dst_text_ptr = text_door_style;
2270
2271     while (*src_text_ptr)
2272     {
2273       if (*src_text_ptr == ' ' ||
2274           *src_text_ptr == '?' ||
2275           *src_text_ptr == '!')
2276         *dst_text_ptr++ = '\n';
2277
2278       if (*src_text_ptr != ' ')
2279         *dst_text_ptr++ = *src_text_ptr;
2280
2281       src_text_ptr++;
2282     }
2283
2284     *dst_text_ptr = '\0';
2285
2286     text_final = text_door_style;
2287   }
2288
2289   setRequestPosition(&sx, &sy, FALSE);
2290
2291   ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2292
2293   for (y = 0; y < y_steps; y++)
2294     for (x = 0; x < x_steps; x++)
2295       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2296                                   x, y, x_steps, y_steps,
2297                                   tile_size, tile_size);
2298
2299   DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2300                  line_length, -1, max_lines, line_spacing, mask_mode,
2301                  request.autowrap, request.centered, FALSE);
2302
2303   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2304     RedrawGadget(tool_gadget[i]);
2305
2306   // store readily prepared envelope request for later use when animating
2307   BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2308
2309   if (text_door_style)
2310     free(text_door_style);
2311 }
2312
2313 void AnimateEnvelopeRequest(int anim_mode, int action)
2314 {
2315   int graphic = IMG_BACKGROUND_REQUEST;
2316   boolean draw_masked = graphic_info[graphic].draw_masked;
2317   int delay_value_normal = request.step_delay;
2318   int delay_value_fast = delay_value_normal / 2;
2319   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2320   boolean no_delay = (tape.warp_forward);
2321   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2322   int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0);
2323   unsigned int anim_delay = 0;
2324
2325   int width = request.width;
2326   int height = request.height;
2327   int tile_size = request.step_offset;
2328   int max_xsize = width  / tile_size;
2329   int max_ysize = height / tile_size;
2330   int max_xsize_inner = max_xsize - 2;
2331   int max_ysize_inner = max_ysize - 2;
2332
2333   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2334   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2335   int xend = max_xsize_inner;
2336   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2337   int xstep = (xstart < xend ? 1 : 0);
2338   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2339   int x, y;
2340
2341   if (setup.quick_doors)
2342   {
2343     xstart = xend;
2344     ystart = yend;
2345   }
2346   else
2347   {
2348     if (action == ACTION_OPENING)
2349       PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2350     else if (action == ACTION_CLOSING)
2351       PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2352   }
2353
2354   for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2355   {
2356     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2357     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2358     int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2359     int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2360     int src_x = sx_center - width  / 2;
2361     int src_y = sy_center - height / 2;
2362     int dst_x = sx_center - xsize * tile_size / 2;
2363     int dst_y = sy_center - ysize * tile_size / 2;
2364     int xsize_size_left = (xsize - 1) * tile_size;
2365     int ysize_size_top  = (ysize - 1) * tile_size;
2366     int max_xsize_pos = (max_xsize - 1) * tile_size;
2367     int max_ysize_pos = (max_ysize - 1) * tile_size;
2368     int xx, yy;
2369
2370     BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2371
2372     for (yy = 0; yy < 2; yy++)
2373     {
2374       for (xx = 0; xx < 2; xx++)
2375       {
2376         int src_xx = src_x + xx * max_xsize_pos;
2377         int src_yy = src_y + yy * max_ysize_pos;
2378         int dst_xx = dst_x + xx * xsize_size_left;
2379         int dst_yy = dst_y + yy * ysize_size_top;
2380         int xx_size = (xx ? tile_size : xsize_size_left);
2381         int yy_size = (yy ? tile_size : ysize_size_top);
2382
2383         if (draw_masked)
2384           BlitBitmapMasked(bitmap_db_cross, backbuffer,
2385                            src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2386         else
2387           BlitBitmap(bitmap_db_cross, backbuffer,
2388                      src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2389       }
2390     }
2391
2392     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2393
2394     DoAnimation();
2395     BackToFront();
2396
2397     WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2398   }
2399 }
2400
2401
2402 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2403 {
2404   int last_game_status = game_status;   /* save current game status */
2405   int graphic = IMG_BACKGROUND_REQUEST;
2406   int sound_opening = SND_REQUEST_OPENING;
2407   int sound_closing = SND_REQUEST_CLOSING;
2408   int anim_mode = graphic_info[graphic].anim_mode;
2409   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2410                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2411
2412   if (game_status == GAME_MODE_PLAYING)
2413     BlitScreenToBitmap(backbuffer);
2414
2415   SetDrawtoField(DRAW_BACKBUFFER);
2416
2417   // SetDrawBackgroundMask(REDRAW_NONE);
2418
2419   if (action == ACTION_OPENING)
2420   {
2421     BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2422
2423     if (req_state & REQ_ASK)
2424     {
2425       MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2426       MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2427     }
2428     else if (req_state & REQ_CONFIRM)
2429     {
2430       MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2431     }
2432     else if (req_state & REQ_PLAYER)
2433     {
2434       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2435       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2436       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2437       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2438     }
2439
2440     DrawEnvelopeRequest(text);
2441
2442     if (game_status != GAME_MODE_MAIN)
2443       InitAnimation();
2444   }
2445
2446   /* force DOOR font inside door area */
2447   game_status = GAME_MODE_PSEUDO_DOOR;
2448
2449   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2450
2451   if (action == ACTION_OPENING)
2452   {
2453     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2454
2455     if (anim_mode == ANIM_DEFAULT)
2456       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2457
2458     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2459
2460   }
2461   else
2462   {
2463     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2464
2465     if (anim_mode != ANIM_NONE)
2466       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2467
2468     if (anim_mode == ANIM_DEFAULT)
2469       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2470   }
2471
2472   game.envelope_active = FALSE;
2473
2474   game_status = last_game_status;       /* restore current game status */
2475
2476   if (action == ACTION_CLOSING)
2477   {
2478     if (game_status != GAME_MODE_MAIN)
2479       StopAnimation();
2480
2481     BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2482   }
2483
2484   // SetDrawBackgroundMask(last_draw_background_mask);
2485
2486   redraw_mask |= REDRAW_FIELD;
2487
2488   if (game_status == GAME_MODE_MAIN)
2489     DoAnimation();
2490
2491   BackToFront();
2492
2493   if (action == ACTION_CLOSING &&
2494       game_status == GAME_MODE_PLAYING &&
2495       level.game_engine_type == GAME_ENGINE_TYPE_RND)
2496     SetDrawtoField(DRAW_BUFFERED);
2497 }
2498
2499 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2500 {
2501   Bitmap *src_bitmap;
2502   int src_x, src_y;
2503   int graphic = el2preimg(element);
2504
2505   getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2506   BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2507 }
2508
2509 void DrawLevel(int draw_background_mask)
2510 {
2511   int x,y;
2512
2513   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2514   SetDrawBackgroundMask(draw_background_mask);
2515
2516   ClearField();
2517
2518   for (x = BX1; x <= BX2; x++)
2519     for (y = BY1; y <= BY2; y++)
2520       DrawScreenField(x, y);
2521
2522   redraw_mask |= REDRAW_FIELD;
2523 }
2524
2525 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2526                     int tilesize)
2527 {
2528   int x,y;
2529
2530   for (x = 0; x < size_x; x++)
2531     for (y = 0; y < size_y; y++)
2532       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2533
2534   redraw_mask |= REDRAW_FIELD;
2535 }
2536
2537 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2538 {
2539   int x,y;
2540
2541   for (x = 0; x < size_x; x++)
2542     for (y = 0; y < size_y; y++)
2543       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2544
2545   redraw_mask |= REDRAW_FIELD;
2546 }
2547
2548 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2549 {
2550   boolean show_level_border = (BorderElement != EL_EMPTY);
2551   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2552   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2553   int tile_size = preview.tile_size;
2554   int preview_width  = preview.xsize * tile_size;
2555   int preview_height = preview.ysize * tile_size;
2556   int real_preview_xsize = MIN(level_xsize, preview.xsize);
2557   int real_preview_ysize = MIN(level_ysize, preview.ysize);
2558   int real_preview_width  = real_preview_xsize * tile_size;
2559   int real_preview_height = real_preview_ysize * tile_size;
2560   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2561   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2562   int x, y;
2563
2564   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2565     return;
2566
2567   DrawBackground(dst_x, dst_y, preview_width, preview_height);
2568
2569   dst_x += (preview_width  - real_preview_width)  / 2;
2570   dst_y += (preview_height - real_preview_height) / 2;
2571
2572   for (x = 0; x < real_preview_xsize; x++)
2573   {
2574     for (y = 0; y < real_preview_ysize; y++)
2575     {
2576       int lx = from_x + x + (show_level_border ? -1 : 0);
2577       int ly = from_y + y + (show_level_border ? -1 : 0);
2578       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2579                      getBorderElement(lx, ly));
2580
2581       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2582                          element, tile_size);
2583     }
2584   }
2585
2586   redraw_mask |= REDRAW_MICROLEVEL;
2587 }
2588
2589 #define MICROLABEL_EMPTY                0
2590 #define MICROLABEL_LEVEL_NAME           1
2591 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
2592 #define MICROLABEL_LEVEL_AUTHOR         3
2593 #define MICROLABEL_IMPORTED_FROM_HEAD   4
2594 #define MICROLABEL_IMPORTED_FROM        5
2595 #define MICROLABEL_IMPORTED_BY_HEAD     6
2596 #define MICROLABEL_IMPORTED_BY          7
2597
2598 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2599 {
2600   int max_text_width = SXSIZE;
2601   int font_width = getFontWidth(font_nr);
2602
2603   if (pos->align == ALIGN_CENTER)
2604     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2605   else if (pos->align == ALIGN_RIGHT)
2606     max_text_width = pos->x;
2607   else
2608     max_text_width = SXSIZE - pos->x;
2609
2610   return max_text_width / font_width;
2611 }
2612
2613 static void DrawPreviewLevelLabelExt(int mode)
2614 {
2615   struct TextPosInfo *pos = &menu.main.text.level_info_2;
2616   char label_text[MAX_OUTPUT_LINESIZE + 1];
2617   int max_len_label_text;
2618   int font_nr = pos->font;
2619   int i;
2620
2621   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2622     return;
2623
2624   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2625       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2626       mode == MICROLABEL_IMPORTED_BY_HEAD)
2627     font_nr = pos->font_alt;
2628
2629   max_len_label_text = getMaxTextLength(pos, font_nr);
2630
2631   if (pos->size != -1)
2632     max_len_label_text = pos->size;
2633
2634   for (i = 0; i < max_len_label_text; i++)
2635     label_text[i] = ' ';
2636   label_text[max_len_label_text] = '\0';
2637
2638   if (strlen(label_text) > 0)
2639     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2640
2641   strncpy(label_text,
2642           (mode == MICROLABEL_LEVEL_NAME ? level.name :
2643            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2644            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2645            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2646            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2647            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2648            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2649           max_len_label_text);
2650   label_text[max_len_label_text] = '\0';
2651
2652   if (strlen(label_text) > 0)
2653     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2654
2655   redraw_mask |= REDRAW_MICROLEVEL;
2656 }
2657
2658 static void DrawPreviewLevelExt(boolean restart)
2659 {
2660   static unsigned int scroll_delay = 0;
2661   static unsigned int label_delay = 0;
2662   static int from_x, from_y, scroll_direction;
2663   static int label_state, label_counter;
2664   unsigned int scroll_delay_value = preview.step_delay;
2665   boolean show_level_border = (BorderElement != EL_EMPTY);
2666   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2667   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2668   int last_game_status = game_status;           /* save current game status */
2669
2670   if (restart)
2671   {
2672     from_x = 0;
2673     from_y = 0;
2674
2675     if (preview.anim_mode == ANIM_CENTERED)
2676     {
2677       if (level_xsize > preview.xsize)
2678         from_x = (level_xsize - preview.xsize) / 2;
2679       if (level_ysize > preview.ysize)
2680         from_y = (level_ysize - preview.ysize) / 2;
2681     }
2682
2683     from_x += preview.xoffset;
2684     from_y += preview.yoffset;
2685
2686     scroll_direction = MV_RIGHT;
2687     label_state = 1;
2688     label_counter = 0;
2689
2690     DrawPreviewLevelPlayfieldExt(from_x, from_y);
2691     DrawPreviewLevelLabelExt(label_state);
2692
2693     /* initialize delay counters */
2694     DelayReached(&scroll_delay, 0);
2695     DelayReached(&label_delay, 0);
2696
2697     if (leveldir_current->name)
2698     {
2699       struct TextPosInfo *pos = &menu.main.text.level_info_1;
2700       char label_text[MAX_OUTPUT_LINESIZE + 1];
2701       int font_nr = pos->font;
2702       int max_len_label_text = getMaxTextLength(pos, font_nr);
2703
2704       if (pos->size != -1)
2705         max_len_label_text = pos->size;
2706
2707       strncpy(label_text, leveldir_current->name, max_len_label_text);
2708       label_text[max_len_label_text] = '\0';
2709
2710       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2711         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2712     }
2713
2714     game_status = last_game_status;     /* restore current game status */
2715
2716     return;
2717   }
2718
2719   /* scroll preview level, if needed */
2720   if (preview.anim_mode != ANIM_NONE &&
2721       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2722       DelayReached(&scroll_delay, scroll_delay_value))
2723   {
2724     switch (scroll_direction)
2725     {
2726       case MV_LEFT:
2727         if (from_x > 0)
2728         {
2729           from_x -= preview.step_offset;
2730           from_x = (from_x < 0 ? 0 : from_x);
2731         }
2732         else
2733           scroll_direction = MV_UP;
2734         break;
2735
2736       case MV_RIGHT:
2737         if (from_x < level_xsize - preview.xsize)
2738         {
2739           from_x += preview.step_offset;
2740           from_x = (from_x > level_xsize - preview.xsize ?
2741                     level_xsize - preview.xsize : from_x);
2742         }
2743         else
2744           scroll_direction = MV_DOWN;
2745         break;
2746
2747       case MV_UP:
2748         if (from_y > 0)
2749         {
2750           from_y -= preview.step_offset;
2751           from_y = (from_y < 0 ? 0 : from_y);
2752         }
2753         else
2754           scroll_direction = MV_RIGHT;
2755         break;
2756
2757       case MV_DOWN:
2758         if (from_y < level_ysize - preview.ysize)
2759         {
2760           from_y += preview.step_offset;
2761           from_y = (from_y > level_ysize - preview.ysize ?
2762                     level_ysize - preview.ysize : from_y);
2763         }
2764         else
2765           scroll_direction = MV_LEFT;
2766         break;
2767
2768       default:
2769         break;
2770     }
2771
2772     DrawPreviewLevelPlayfieldExt(from_x, from_y);
2773   }
2774
2775   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2776   /* redraw micro level label, if needed */
2777   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2778       !strEqual(level.author, ANONYMOUS_NAME) &&
2779       !strEqual(level.author, leveldir_current->name) &&
2780       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2781   {
2782     int max_label_counter = 23;
2783
2784     if (leveldir_current->imported_from != NULL &&
2785         strlen(leveldir_current->imported_from) > 0)
2786       max_label_counter += 14;
2787     if (leveldir_current->imported_by != NULL &&
2788         strlen(leveldir_current->imported_by) > 0)
2789       max_label_counter += 14;
2790
2791     label_counter = (label_counter + 1) % max_label_counter;
2792     label_state = (label_counter >= 0 && label_counter <= 7 ?
2793                    MICROLABEL_LEVEL_NAME :
2794                    label_counter >= 9 && label_counter <= 12 ?
2795                    MICROLABEL_LEVEL_AUTHOR_HEAD :
2796                    label_counter >= 14 && label_counter <= 21 ?
2797                    MICROLABEL_LEVEL_AUTHOR :
2798                    label_counter >= 23 && label_counter <= 26 ?
2799                    MICROLABEL_IMPORTED_FROM_HEAD :
2800                    label_counter >= 28 && label_counter <= 35 ?
2801                    MICROLABEL_IMPORTED_FROM :
2802                    label_counter >= 37 && label_counter <= 40 ?
2803                    MICROLABEL_IMPORTED_BY_HEAD :
2804                    label_counter >= 42 && label_counter <= 49 ?
2805                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2806
2807     if (leveldir_current->imported_from == NULL &&
2808         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2809          label_state == MICROLABEL_IMPORTED_FROM))
2810       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2811                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2812
2813     DrawPreviewLevelLabelExt(label_state);
2814   }
2815
2816   game_status = last_game_status;       /* restore current game status */
2817 }
2818
2819 void DrawPreviewLevelInitial()
2820 {
2821   DrawPreviewLevelExt(TRUE);
2822 }
2823
2824 void DrawPreviewLevelAnimation()
2825 {
2826   DrawPreviewLevelExt(FALSE);
2827 }
2828
2829 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2830                                     int graphic, int sync_frame, int mask_mode)
2831 {
2832   int frame = getGraphicAnimationFrame(graphic, sync_frame);
2833
2834   if (mask_mode == USE_MASKING)
2835     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2836   else
2837     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2838 }
2839
2840 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2841                                          int graphic, int sync_frame,
2842                                          int mask_mode)
2843 {
2844   int frame = getGraphicAnimationFrame(graphic, sync_frame);
2845
2846   if (mask_mode == USE_MASKING)
2847     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2848   else
2849     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2850 }
2851
2852 inline void DrawGraphicAnimation(int x, int y, int graphic)
2853 {
2854   int lx = LEVELX(x), ly = LEVELY(y);
2855
2856   if (!IN_SCR_FIELD(x, y))
2857     return;
2858
2859   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2860                           graphic, GfxFrame[lx][ly], NO_MASKING);
2861
2862   MarkTileDirty(x, y);
2863 }
2864
2865 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2866 {
2867   int lx = LEVELX(x), ly = LEVELY(y);
2868
2869   if (!IN_SCR_FIELD(x, y))
2870     return;
2871
2872   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2873                           graphic, GfxFrame[lx][ly], NO_MASKING);
2874   MarkTileDirty(x, y);
2875 }
2876
2877 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2878 {
2879   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2880 }
2881
2882 void DrawLevelElementAnimation(int x, int y, int element)
2883 {
2884   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2885
2886   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2887 }
2888
2889 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2890 {
2891   int sx = SCREENX(x), sy = SCREENY(y);
2892
2893   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2894     return;
2895
2896   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2897     return;
2898
2899   DrawGraphicAnimation(sx, sy, graphic);
2900
2901 #if 1
2902   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2903     DrawLevelFieldCrumbled(x, y);
2904 #else
2905   if (GFX_CRUMBLED(Feld[x][y]))
2906     DrawLevelFieldCrumbled(x, y);
2907 #endif
2908 }
2909
2910 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2911 {
2912   int sx = SCREENX(x), sy = SCREENY(y);
2913   int graphic;
2914
2915   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2916     return;
2917
2918   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2919
2920   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2921     return;
2922
2923   DrawGraphicAnimation(sx, sy, graphic);
2924
2925   if (GFX_CRUMBLED(element))
2926     DrawLevelFieldCrumbled(x, y);
2927 }
2928
2929 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2930 {
2931   if (player->use_murphy)
2932   {
2933     /* this works only because currently only one player can be "murphy" ... */
2934     static int last_horizontal_dir = MV_LEFT;
2935     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2936
2937     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2938       last_horizontal_dir = move_dir;
2939
2940     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
2941     {
2942       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2943
2944       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2945     }
2946
2947     return graphic;
2948   }
2949   else
2950     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2951 }
2952
2953 static boolean equalGraphics(int graphic1, int graphic2)
2954 {
2955   struct GraphicInfo *g1 = &graphic_info[graphic1];
2956   struct GraphicInfo *g2 = &graphic_info[graphic2];
2957
2958   return (g1->bitmap      == g2->bitmap &&
2959           g1->src_x       == g2->src_x &&
2960           g1->src_y       == g2->src_y &&
2961           g1->anim_frames == g2->anim_frames &&
2962           g1->anim_delay  == g2->anim_delay &&
2963           g1->anim_mode   == g2->anim_mode);
2964 }
2965
2966 void DrawAllPlayers()
2967 {
2968   int i;
2969
2970   for (i = 0; i < MAX_PLAYERS; i++)
2971     if (stored_player[i].active)
2972       DrawPlayer(&stored_player[i]);
2973 }
2974
2975 void DrawPlayerField(int x, int y)
2976 {
2977   if (!IS_PLAYER(x, y))
2978     return;
2979
2980   DrawPlayer(PLAYERINFO(x, y));
2981 }
2982
2983 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2984
2985 void DrawPlayer(struct PlayerInfo *player)
2986 {
2987   int jx = player->jx;
2988   int jy = player->jy;
2989   int move_dir = player->MovDir;
2990   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
2991   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
2992   int last_jx = (player->is_moving ? jx - dx : jx);
2993   int last_jy = (player->is_moving ? jy - dy : jy);
2994   int next_jx = jx + dx;
2995   int next_jy = jy + dy;
2996   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
2997   boolean player_is_opaque = FALSE;
2998   int sx = SCREENX(jx), sy = SCREENY(jy);
2999   int sxx = 0, syy = 0;
3000   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3001   int graphic;
3002   int action = ACTION_DEFAULT;
3003   int last_player_graphic = getPlayerGraphic(player, move_dir);
3004   int last_player_frame = player->Frame;
3005   int frame = 0;
3006
3007   /* GfxElement[][] is set to the element the player is digging or collecting;
3008      remove also for off-screen player if the player is not moving anymore */
3009   if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3010     GfxElement[jx][jy] = EL_UNDEFINED;
3011
3012   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3013     return;
3014
3015 #if DEBUG
3016   if (!IN_LEV_FIELD(jx, jy))
3017   {
3018     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3019     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3020     printf("DrawPlayerField(): This should never happen!\n");
3021     return;
3022   }
3023 #endif
3024
3025   if (element == EL_EXPLOSION)
3026     return;
3027
3028   action = (player->is_pushing    ? ACTION_PUSHING         :
3029             player->is_digging    ? ACTION_DIGGING         :
3030             player->is_collecting ? ACTION_COLLECTING      :
3031             player->is_moving     ? ACTION_MOVING          :
3032             player->is_snapping   ? ACTION_SNAPPING        :
3033             player->is_dropping   ? ACTION_DROPPING        :
3034             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
3035
3036   if (player->is_waiting)
3037     move_dir = player->dir_waiting;
3038
3039   InitPlayerGfxAnimation(player, action, move_dir);
3040
3041   /* ----------------------------------------------------------------------- */
3042   /* draw things in the field the player is leaving, if needed               */
3043   /* ----------------------------------------------------------------------- */
3044
3045   if (player->is_moving)
3046   {
3047     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3048     {
3049       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3050
3051       if (last_element == EL_DYNAMITE_ACTIVE ||
3052           last_element == EL_EM_DYNAMITE_ACTIVE ||
3053           last_element == EL_SP_DISK_RED_ACTIVE)
3054         DrawDynamite(last_jx, last_jy);
3055       else
3056         DrawLevelFieldThruMask(last_jx, last_jy);
3057     }
3058     else if (last_element == EL_DYNAMITE_ACTIVE ||
3059              last_element == EL_EM_DYNAMITE_ACTIVE ||
3060              last_element == EL_SP_DISK_RED_ACTIVE)
3061       DrawDynamite(last_jx, last_jy);
3062 #if 0
3063     /* !!! this is not enough to prevent flickering of players which are
3064        moving next to each others without a free tile between them -- this
3065        can only be solved by drawing all players layer by layer (first the
3066        background, then the foreground etc.) !!! => TODO */
3067     else if (!IS_PLAYER(last_jx, last_jy))
3068       DrawLevelField(last_jx, last_jy);
3069 #else
3070     else
3071       DrawLevelField(last_jx, last_jy);
3072 #endif
3073
3074     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3075       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3076   }
3077
3078   if (!IN_SCR_FIELD(sx, sy))
3079     return;
3080
3081   /* ----------------------------------------------------------------------- */
3082   /* draw things behind the player, if needed                                */
3083   /* ----------------------------------------------------------------------- */
3084
3085   if (Back[jx][jy])
3086     DrawLevelElement(jx, jy, Back[jx][jy]);
3087   else if (IS_ACTIVE_BOMB(element))
3088     DrawLevelElement(jx, jy, EL_EMPTY);
3089   else
3090   {
3091     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3092     {
3093       int old_element = GfxElement[jx][jy];
3094       int old_graphic = el_act_dir2img(old_element, action, move_dir);
3095       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3096
3097       if (GFX_CRUMBLED(old_element))
3098         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3099       else
3100         DrawGraphic(sx, sy, old_graphic, frame);
3101
3102       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3103         player_is_opaque = TRUE;
3104     }
3105     else
3106     {
3107       GfxElement[jx][jy] = EL_UNDEFINED;
3108
3109       /* make sure that pushed elements are drawn with correct frame rate */
3110       graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3111
3112       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3113         GfxFrame[jx][jy] = player->StepFrame;
3114
3115       DrawLevelField(jx, jy);
3116     }
3117   }
3118
3119 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3120   /* ----------------------------------------------------------------------- */
3121   /* draw player himself                                                     */
3122   /* ----------------------------------------------------------------------- */
3123
3124   graphic = getPlayerGraphic(player, move_dir);
3125
3126   /* in the case of changed player action or direction, prevent the current
3127      animation frame from being restarted for identical animations */
3128   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3129     player->Frame = last_player_frame;
3130
3131   frame = getGraphicAnimationFrame(graphic, player->Frame);
3132
3133   if (player->GfxPos)
3134   {
3135     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3136       sxx = player->GfxPos;
3137     else
3138       syy = player->GfxPos;
3139   }
3140
3141   if (!setup.soft_scrolling && ScreenMovPos)
3142     sxx = syy = 0;
3143
3144   if (player_is_opaque)
3145     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3146   else
3147     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3148
3149   if (SHIELD_ON(player))
3150   {
3151     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3152                    IMG_SHIELD_NORMAL_ACTIVE);
3153     int frame = getGraphicAnimationFrame(graphic, -1);
3154
3155     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3156   }
3157 #endif
3158
3159 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3160   if (player->GfxPos)
3161   {
3162     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3163       sxx = player->GfxPos;
3164     else
3165       syy = player->GfxPos;
3166   }
3167 #endif
3168
3169   /* ----------------------------------------------------------------------- */
3170   /* draw things the player is pushing, if needed                            */
3171   /* ----------------------------------------------------------------------- */
3172
3173   if (player->is_pushing && player->is_moving)
3174   {
3175     int px = SCREENX(jx), py = SCREENY(jy);
3176     int pxx = (TILEX - ABS(sxx)) * dx;
3177     int pyy = (TILEY - ABS(syy)) * dy;
3178     int gfx_frame = GfxFrame[jx][jy];
3179
3180     int graphic;
3181     int sync_frame;
3182     int frame;
3183
3184     if (!IS_MOVING(jx, jy))             /* push movement already finished */
3185     {
3186       element = Feld[next_jx][next_jy];
3187       gfx_frame = GfxFrame[next_jx][next_jy];
3188     }
3189
3190     graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3191
3192     sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3193     frame = getGraphicAnimationFrame(graphic, sync_frame);
3194
3195     /* draw background element under pushed element (like the Sokoban field) */
3196     if (game.use_masked_pushing && IS_MOVING(jx, jy))
3197     {
3198       /* this allows transparent pushing animation over non-black background */
3199
3200       if (Back[jx][jy])
3201         DrawLevelElement(jx, jy, Back[jx][jy]);
3202       else
3203         DrawLevelElement(jx, jy, EL_EMPTY);
3204
3205       if (Back[next_jx][next_jy])
3206         DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3207       else
3208         DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3209     }
3210     else if (Back[next_jx][next_jy])
3211       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3212
3213 #if 1
3214     /* do not draw (EM style) pushing animation when pushing is finished */
3215     /* (two-tile animations usually do not contain start and end frame) */
3216     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3217       DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3218     else
3219       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3220 #else
3221     /* masked drawing is needed for EMC style (double) movement graphics */
3222     /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3223     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3224 #endif
3225   }
3226
3227 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3228   /* ----------------------------------------------------------------------- */
3229   /* draw player himself                                                     */
3230   /* ----------------------------------------------------------------------- */
3231
3232   graphic = getPlayerGraphic(player, move_dir);
3233
3234   /* in the case of changed player action or direction, prevent the current
3235      animation frame from being restarted for identical animations */
3236   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3237     player->Frame = last_player_frame;
3238
3239   frame = getGraphicAnimationFrame(graphic, player->Frame);
3240
3241   if (player->GfxPos)
3242   {
3243     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3244       sxx = player->GfxPos;
3245     else
3246       syy = player->GfxPos;
3247   }
3248
3249   if (!setup.soft_scrolling && ScreenMovPos)
3250     sxx = syy = 0;
3251
3252   if (player_is_opaque)
3253     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3254   else
3255     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3256
3257   if (SHIELD_ON(player))
3258   {
3259     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3260                    IMG_SHIELD_NORMAL_ACTIVE);
3261     int frame = getGraphicAnimationFrame(graphic, -1);
3262
3263     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3264   }
3265 #endif
3266
3267   /* ----------------------------------------------------------------------- */
3268   /* draw things in front of player (active dynamite or dynabombs)           */
3269   /* ----------------------------------------------------------------------- */
3270
3271   if (IS_ACTIVE_BOMB(element))
3272   {
3273     graphic = el2img(element);
3274     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3275
3276     if (game.emulation == EMU_SUPAPLEX)
3277       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3278     else
3279       DrawGraphicThruMask(sx, sy, graphic, frame);
3280   }
3281
3282   if (player_is_moving && last_element == EL_EXPLOSION)
3283   {
3284     int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3285                    GfxElement[last_jx][last_jy] :  EL_EMPTY);
3286     int graphic = el_act2img(element, ACTION_EXPLODING);
3287     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3288     int phase = ExplodePhase[last_jx][last_jy] - 1;
3289     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3290
3291     if (phase >= delay)
3292       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3293   }
3294
3295   /* ----------------------------------------------------------------------- */
3296   /* draw elements the player is just walking/passing through/under          */
3297   /* ----------------------------------------------------------------------- */
3298
3299   if (player_is_moving)
3300   {
3301     /* handle the field the player is leaving ... */
3302     if (IS_ACCESSIBLE_INSIDE(last_element))
3303       DrawLevelField(last_jx, last_jy);
3304     else if (IS_ACCESSIBLE_UNDER(last_element))
3305       DrawLevelFieldThruMask(last_jx, last_jy);
3306   }
3307
3308   /* do not redraw accessible elements if the player is just pushing them */
3309   if (!player_is_moving || !player->is_pushing)
3310   {
3311     /* ... and the field the player is entering */
3312     if (IS_ACCESSIBLE_INSIDE(element))
3313       DrawLevelField(jx, jy);
3314     else if (IS_ACCESSIBLE_UNDER(element))
3315       DrawLevelFieldThruMask(jx, jy);
3316   }
3317
3318   MarkTileDirty(sx, sy);
3319 }
3320
3321 /* ------------------------------------------------------------------------- */
3322
3323 void WaitForEventToContinue()
3324 {
3325   boolean still_wait = TRUE;
3326
3327   /* simulate releasing mouse button over last gadget, if still pressed */
3328   if (button_status)
3329     HandleGadgets(-1, -1, 0);
3330
3331   button_status = MB_RELEASED;
3332
3333   ClearEventQueue();
3334
3335   while (still_wait)
3336   {
3337     if (PendingEvent())
3338     {
3339       Event event;
3340
3341       NextEvent(&event);
3342
3343       switch (event.type)
3344       {
3345         case EVENT_BUTTONPRESS:
3346         case EVENT_KEYPRESS:
3347           still_wait = FALSE;
3348           break;
3349
3350         case EVENT_KEYRELEASE:
3351           ClearPlayerAction();
3352           break;
3353
3354         default:
3355           HandleOtherEvents(&event);
3356           break;
3357       }
3358     }
3359     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3360     {
3361       still_wait = FALSE;
3362     }
3363
3364     DoAnimation();
3365
3366     /* don't eat all CPU time */
3367     Delay(10);
3368   }
3369 }
3370
3371 #define MAX_REQUEST_LINES               13
3372 #define MAX_REQUEST_LINE_FONT1_LEN      7
3373 #define MAX_REQUEST_LINE_FONT2_LEN      10
3374
3375 static int RequestHandleEvents(unsigned int req_state)
3376 {
3377   int last_game_status = game_status;   /* save current game status */
3378   int result;
3379   int mx, my;
3380
3381   button_status = MB_RELEASED;
3382
3383   request_gadget_id = -1;
3384   result = -1;
3385
3386   while (result < 0)
3387   {
3388     if (PendingEvent())
3389     {
3390       Event event;
3391
3392       NextEvent(&event);
3393
3394       switch (event.type)
3395       {
3396         case EVENT_BUTTONPRESS:
3397         case EVENT_BUTTONRELEASE:
3398         case EVENT_MOTIONNOTIFY:
3399         {
3400           if (event.type == EVENT_MOTIONNOTIFY)
3401           {
3402             if (!PointerInWindow(window))
3403               continue; /* window and pointer are on different screens */
3404
3405             if (!button_status)
3406               continue;
3407
3408             motion_status = TRUE;
3409             mx = ((MotionEvent *) &event)->x;
3410             my = ((MotionEvent *) &event)->y;
3411           }
3412           else
3413           {
3414             motion_status = FALSE;
3415             mx = ((ButtonEvent *) &event)->x;
3416             my = ((ButtonEvent *) &event)->y;
3417             if (event.type == EVENT_BUTTONPRESS)
3418               button_status = ((ButtonEvent *) &event)->button;
3419             else
3420               button_status = MB_RELEASED;
3421           }
3422
3423           /* this sets 'request_gadget_id' */
3424           HandleGadgets(mx, my, button_status);
3425
3426           switch (request_gadget_id)
3427           {
3428             case TOOL_CTRL_ID_YES:
3429               result = TRUE;
3430               break;
3431             case TOOL_CTRL_ID_NO:
3432               result = FALSE;
3433               break;
3434             case TOOL_CTRL_ID_CONFIRM:
3435               result = TRUE | FALSE;
3436               break;
3437
3438             case TOOL_CTRL_ID_PLAYER_1:
3439               result = 1;
3440               break;
3441             case TOOL_CTRL_ID_PLAYER_2:
3442               result = 2;
3443               break;
3444             case TOOL_CTRL_ID_PLAYER_3:
3445               result = 3;
3446               break;
3447             case TOOL_CTRL_ID_PLAYER_4:
3448               result = 4;
3449               break;
3450
3451             default:
3452               break;
3453           }
3454
3455           break;
3456         }
3457
3458         case EVENT_KEYPRESS:
3459           switch (GetEventKey((KeyEvent *)&event, TRUE))
3460           {
3461             case KSYM_space:
3462               if (req_state & REQ_CONFIRM)
3463                 result = 1;
3464               break;
3465
3466             case KSYM_Return:
3467 #if defined(TARGET_SDL2)
3468             case KSYM_Menu:
3469 #endif
3470               result = 1;
3471               break;
3472
3473             case KSYM_Escape:
3474 #if defined(TARGET_SDL2)
3475             case KSYM_Back:
3476 #endif
3477               result = 0;
3478               break;
3479
3480             default:
3481               break;
3482           }
3483
3484           if (req_state & REQ_PLAYER)
3485             result = 0;
3486           break;
3487
3488         case EVENT_KEYRELEASE:
3489           ClearPlayerAction();
3490           break;
3491
3492         default:
3493           HandleOtherEvents(&event);
3494           break;
3495       }
3496     }
3497     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3498     {
3499       int joy = AnyJoystick();
3500
3501       if (joy & JOY_BUTTON_1)
3502         result = 1;
3503       else if (joy & JOY_BUTTON_2)
3504         result = 0;
3505     }
3506
3507     if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3508     {
3509       HandleGameActions();
3510     }
3511     else
3512     {
3513       DoAnimation();
3514
3515       if (!PendingEvent())      /* delay only if no pending events */
3516         Delay(10);
3517     }
3518
3519     game_status = GAME_MODE_PSEUDO_DOOR;
3520
3521     BackToFront();
3522
3523     game_status = last_game_status;     /* restore current game status */
3524   }
3525
3526   return result;
3527 }
3528
3529 static boolean RequestDoor(char *text, unsigned int req_state)
3530 {
3531   unsigned int old_door_state;
3532   int last_game_status = game_status;   /* save current game status */
3533   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3534   int font_nr = FONT_TEXT_2;
3535   char *text_ptr;
3536   int result;
3537   int ty;
3538
3539   if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3540   {
3541     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3542     font_nr = FONT_TEXT_1;
3543   }
3544
3545   if (game_status == GAME_MODE_PLAYING)
3546     BlitScreenToBitmap(backbuffer);
3547
3548   /* disable deactivated drawing when quick-loading level tape recording */
3549   if (tape.playing && tape.deactivate_display)
3550     TapeDeactivateDisplayOff(TRUE);
3551
3552   SetMouseCursor(CURSOR_DEFAULT);
3553
3554 #if defined(NETWORK_AVALIABLE)
3555   /* pause network game while waiting for request to answer */
3556   if (options.network &&
3557       game_status == GAME_MODE_PLAYING &&
3558       req_state & REQUEST_WAIT_FOR_INPUT)
3559     SendToServer_PausePlaying();
3560 #endif
3561
3562   old_door_state = GetDoorState();
3563
3564   /* simulate releasing mouse button over last gadget, if still pressed */
3565   if (button_status)
3566     HandleGadgets(-1, -1, 0);
3567
3568   UnmapAllGadgets();
3569
3570   /* draw released gadget before proceeding */
3571   // BackToFront();
3572
3573   if (old_door_state & DOOR_OPEN_1)
3574   {
3575     CloseDoor(DOOR_CLOSE_1);
3576
3577     /* save old door content */
3578     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3579                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3580   }
3581
3582   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3583   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3584
3585   /* clear door drawing field */
3586   DrawBackground(DX, DY, DXSIZE, DYSIZE);
3587
3588   /* force DOOR font inside door area */
3589   game_status = GAME_MODE_PSEUDO_DOOR;
3590
3591   /* write text for request */
3592   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3593   {
3594     char text_line[max_request_line_len + 1];
3595     int tx, tl, tc = 0;
3596
3597     if (!*text_ptr)
3598       break;
3599
3600     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3601     {
3602       tc = *(text_ptr + tx);
3603       // if (!tc || tc == ' ')
3604       if (!tc || tc == ' ' || tc == '?' || tc == '!')
3605         break;
3606     }
3607
3608     if ((tc == '?' || tc == '!') && tl == 0)
3609       tl = 1;
3610
3611     if (!tl)
3612     { 
3613       text_ptr++; 
3614       ty--; 
3615       continue; 
3616     }
3617
3618     strncpy(text_line, text_ptr, tl);
3619     text_line[tl] = 0;
3620
3621     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3622              DY + 8 + ty * (getFontHeight(font_nr) + 2),
3623              text_line, font_nr);
3624
3625     text_ptr += tl + (tc == ' ' ? 1 : 0);
3626     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3627   }
3628
3629   game_status = last_game_status;       /* restore current game status */
3630
3631   if (req_state & REQ_ASK)
3632   {
3633     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3634     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3635   }
3636   else if (req_state & REQ_CONFIRM)
3637   {
3638     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3639   }
3640   else if (req_state & REQ_PLAYER)
3641   {
3642     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3643     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3644     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3645     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3646   }
3647
3648   /* copy request gadgets to door backbuffer */
3649   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3650
3651   OpenDoor(DOOR_OPEN_1);
3652
3653   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3654   {
3655     if (game_status == GAME_MODE_PLAYING)
3656     {
3657       SetPanelBackground();
3658       SetDrawBackgroundMask(REDRAW_DOOR_1);
3659     }
3660     else
3661     {
3662       SetDrawBackgroundMask(REDRAW_FIELD);
3663     }
3664
3665     return FALSE;
3666   }
3667
3668   if (game_status != GAME_MODE_MAIN)
3669     InitAnimation();
3670
3671   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3672
3673   // ---------- handle request buttons ----------
3674   result = RequestHandleEvents(req_state);
3675
3676   if (game_status != GAME_MODE_MAIN)
3677     StopAnimation();
3678
3679   UnmapToolButtons();
3680
3681   if (!(req_state & REQ_STAY_OPEN))
3682   {
3683     CloseDoor(DOOR_CLOSE_1);
3684
3685     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3686         (req_state & REQ_REOPEN))
3687       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3688   }
3689
3690   RemapAllGadgets();
3691
3692   if (game_status == GAME_MODE_PLAYING)
3693   {
3694     SetPanelBackground();
3695     SetDrawBackgroundMask(REDRAW_DOOR_1);
3696   }
3697   else
3698   {
3699     SetDrawBackgroundMask(REDRAW_FIELD);
3700   }
3701
3702 #if defined(NETWORK_AVALIABLE)
3703   /* continue network game after request */
3704   if (options.network &&
3705       game_status == GAME_MODE_PLAYING &&
3706       req_state & REQUEST_WAIT_FOR_INPUT)
3707     SendToServer_ContinuePlaying();
3708 #endif
3709
3710   /* restore deactivated drawing when quick-loading level tape recording */
3711   if (tape.playing && tape.deactivate_display)
3712     TapeDeactivateDisplayOn();
3713
3714   return result;
3715 }
3716
3717 static boolean RequestEnvelope(char *text, unsigned int req_state)
3718 {
3719   int result;
3720
3721   if (game_status == GAME_MODE_PLAYING)
3722     BlitScreenToBitmap(backbuffer);
3723
3724   /* disable deactivated drawing when quick-loading level tape recording */
3725   if (tape.playing && tape.deactivate_display)
3726     TapeDeactivateDisplayOff(TRUE);
3727
3728   SetMouseCursor(CURSOR_DEFAULT);
3729
3730 #if defined(NETWORK_AVALIABLE)
3731   /* pause network game while waiting for request to answer */
3732   if (options.network &&
3733       game_status == GAME_MODE_PLAYING &&
3734       req_state & REQUEST_WAIT_FOR_INPUT)
3735     SendToServer_PausePlaying();
3736 #endif
3737
3738   /* simulate releasing mouse button over last gadget, if still pressed */
3739   if (button_status)
3740     HandleGadgets(-1, -1, 0);
3741
3742   UnmapAllGadgets();
3743
3744   // (replace with setting corresponding request background)
3745   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3746   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3747
3748   /* clear door drawing field */
3749   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3750
3751   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3752
3753   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3754   {
3755     if (game_status == GAME_MODE_PLAYING)
3756     {
3757       SetPanelBackground();
3758       SetDrawBackgroundMask(REDRAW_DOOR_1);
3759     }
3760     else
3761     {
3762       SetDrawBackgroundMask(REDRAW_FIELD);
3763     }
3764
3765     return FALSE;
3766   }
3767
3768   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3769
3770   // ---------- handle request buttons ----------
3771   result = RequestHandleEvents(req_state);
3772
3773   if (game_status != GAME_MODE_MAIN)
3774     StopAnimation();
3775
3776   UnmapToolButtons();
3777
3778   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3779
3780   RemapAllGadgets();
3781
3782   if (game_status == GAME_MODE_PLAYING)
3783   {
3784     SetPanelBackground();
3785     SetDrawBackgroundMask(REDRAW_DOOR_1);
3786   }
3787   else
3788   {
3789     SetDrawBackgroundMask(REDRAW_FIELD);
3790   }
3791
3792 #if defined(NETWORK_AVALIABLE)
3793   /* continue network game after request */
3794   if (options.network &&
3795       game_status == GAME_MODE_PLAYING &&
3796       req_state & REQUEST_WAIT_FOR_INPUT)
3797     SendToServer_ContinuePlaying();
3798 #endif
3799
3800   /* restore deactivated drawing when quick-loading level tape recording */
3801   if (tape.playing && tape.deactivate_display)
3802     TapeDeactivateDisplayOn();
3803
3804   return result;
3805 }
3806
3807 boolean Request(char *text, unsigned int req_state)
3808 {
3809   if (global.use_envelope_request)
3810     return RequestEnvelope(text, req_state);
3811   else
3812     return RequestDoor(text, req_state);
3813 }
3814
3815 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3816 {
3817   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3818   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3819   int compare_result;
3820
3821   if (dpo1->sort_priority != dpo2->sort_priority)
3822     compare_result = dpo1->sort_priority - dpo2->sort_priority;
3823   else
3824     compare_result = dpo1->nr - dpo2->nr;
3825
3826   return compare_result;
3827 }
3828
3829 void InitGraphicCompatibilityInfo_Doors()
3830 {
3831   struct
3832   {
3833     int door_token;
3834     int part_1, part_8;
3835     struct DoorInfo *door;
3836   }
3837   doors[] =
3838   {
3839     { DOOR_1,   IMG_DOOR_1_GFX_PART_1,  IMG_DOOR_1_GFX_PART_8,  &door_1 },
3840     { DOOR_2,   IMG_DOOR_2_GFX_PART_1,  IMG_DOOR_2_GFX_PART_8,  &door_2 },
3841
3842     { -1,       -1,                     -1,                     NULL    }
3843   };
3844   struct Rect door_rect_list[] =
3845   {
3846     { DX, DY, DXSIZE, DYSIZE },
3847     { VX, VY, VXSIZE, VYSIZE }
3848   };
3849   int i, j;
3850
3851   for (i = 0; doors[i].door_token != -1; i++)
3852   {
3853     int door_token = doors[i].door_token;
3854     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3855     int part_1 = doors[i].part_1;
3856     int part_8 = doors[i].part_8;
3857     int part_2 = part_1 + 1;
3858     int part_3 = part_1 + 2;
3859     struct DoorInfo *door = doors[i].door;
3860     struct Rect *door_rect = &door_rect_list[door_index];
3861     boolean door_gfx_redefined = FALSE;
3862
3863     /* check if any door part graphic definitions have been redefined */
3864
3865     for (j = 0; door_part_controls[j].door_token != -1; j++)
3866     {
3867       struct DoorPartControlInfo *dpc = &door_part_controls[j];
3868       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3869
3870       if (dpc->door_token == door_token && fi->redefined)
3871         door_gfx_redefined = TRUE;
3872     }
3873
3874     /* check for old-style door graphic/animation modifications */
3875
3876     if (!door_gfx_redefined)
3877     {
3878       if (door->anim_mode & ANIM_STATIC_PANEL)
3879       {
3880         door->panel.step_xoffset = 0;
3881         door->panel.step_yoffset = 0;
3882       }
3883
3884       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3885       {
3886         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3887         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3888         int num_door_steps, num_panel_steps;
3889
3890         /* remove door part graphics other than the two default wings */
3891
3892         for (j = 0; door_part_controls[j].door_token != -1; j++)
3893         {
3894           struct DoorPartControlInfo *dpc = &door_part_controls[j];
3895           struct GraphicInfo *g = &graphic_info[dpc->graphic];
3896
3897           if (dpc->graphic >= part_3 &&
3898               dpc->graphic <= part_8)
3899             g->bitmap = NULL;
3900         }
3901
3902         /* set graphics and screen positions of the default wings */
3903
3904         g_part_1->width  = door_rect->width;
3905         g_part_1->height = door_rect->height;
3906         g_part_2->width  = door_rect->width;
3907         g_part_2->height = door_rect->height;
3908         g_part_2->src_x = door_rect->width;
3909         g_part_2->src_y = g_part_1->src_y;
3910
3911         door->part_2.x = door->part_1.x;
3912         door->part_2.y = door->part_1.y;
3913
3914         if (door->width != -1)
3915         {
3916           g_part_1->width = door->width;
3917           g_part_2->width = door->width;
3918
3919           // special treatment for graphics and screen position of right wing
3920           g_part_2->src_x += door_rect->width - door->width;
3921           door->part_2.x  += door_rect->width - door->width;
3922         }
3923
3924         if (door->height != -1)
3925         {
3926           g_part_1->height = door->height;
3927           g_part_2->height = door->height;
3928
3929           // special treatment for graphics and screen position of bottom wing
3930           g_part_2->src_y += door_rect->height - door->height;
3931           door->part_2.y  += door_rect->height - door->height;
3932         }
3933
3934         /* set animation delays for the default wings and panels */
3935
3936         door->part_1.step_delay = door->step_delay;
3937         door->part_2.step_delay = door->step_delay;
3938         door->panel.step_delay  = door->step_delay;
3939
3940         /* set animation draw order for the default wings */
3941
3942         door->part_1.sort_priority = 2; /* draw left wing over ... */
3943         door->part_2.sort_priority = 1; /*          ... right wing */
3944
3945         /* set animation draw offset for the default wings */
3946
3947         if (door->anim_mode & ANIM_HORIZONTAL)
3948         {
3949           door->part_1.step_xoffset = door->step_offset;
3950           door->part_1.step_yoffset = 0;
3951           door->part_2.step_xoffset = door->step_offset * -1;
3952           door->part_2.step_yoffset = 0;
3953
3954           num_door_steps = g_part_1->width / door->step_offset;
3955         }
3956         else    // ANIM_VERTICAL
3957         {
3958           door->part_1.step_xoffset = 0;
3959           door->part_1.step_yoffset = door->step_offset;
3960           door->part_2.step_xoffset = 0;
3961           door->part_2.step_yoffset = door->step_offset * -1;
3962
3963           num_door_steps = g_part_1->height / door->step_offset;
3964         }
3965
3966         /* set animation draw offset for the default panels */
3967
3968         if (door->step_offset > 1)
3969         {
3970           num_panel_steps = 2 * door_rect->height / door->step_offset;
3971           door->panel.start_step = num_panel_steps - num_door_steps;
3972           door->panel.start_step_closing = door->panel.start_step;
3973         }
3974         else
3975         {
3976           num_panel_steps = door_rect->height / door->step_offset;
3977           door->panel.start_step = num_panel_steps - num_door_steps / 2;
3978           door->panel.start_step_closing = door->panel.start_step;
3979           door->panel.step_delay *= 2;
3980         }
3981       }
3982     }
3983   }
3984 }
3985
3986 void InitDoors()
3987 {
3988   int i;
3989
3990   for (i = 0; door_part_controls[i].door_token != -1; i++)
3991   {
3992     struct DoorPartControlInfo *dpc = &door_part_controls[i];
3993     struct DoorPartOrderInfo *dpo = &door_part_order[i];
3994
3995     /* initialize "start_step_opening" and "start_step_closing", if needed */
3996     if (dpc->pos->start_step_opening == 0 &&
3997         dpc->pos->start_step_closing == 0)
3998     {
3999       // dpc->pos->start_step_opening = dpc->pos->start_step;
4000       dpc->pos->start_step_closing = dpc->pos->start_step;
4001     }
4002
4003     /* fill structure for door part draw order (sorted below) */
4004     dpo->nr = i;
4005     dpo->sort_priority = dpc->pos->sort_priority;
4006   }
4007
4008   /* sort door part controls according to sort_priority and graphic number */
4009   qsort(door_part_order, MAX_DOOR_PARTS,
4010         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4011 }
4012
4013 unsigned int OpenDoor(unsigned int door_state)
4014 {
4015   if (door_state & DOOR_COPY_BACK)
4016   {
4017     if (door_state & DOOR_OPEN_1)
4018       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4019                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4020
4021     if (door_state & DOOR_OPEN_2)
4022       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4023                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4024
4025     door_state &= ~DOOR_COPY_BACK;
4026   }
4027
4028   return MoveDoor(door_state);
4029 }
4030
4031 unsigned int CloseDoor(unsigned int door_state)
4032 {
4033   unsigned int old_door_state = GetDoorState();
4034
4035   if (!(door_state & DOOR_NO_COPY_BACK))
4036   {
4037     if (old_door_state & DOOR_OPEN_1)
4038       BlitBitmap(backbuffer, bitmap_db_door_1,
4039                  DX, DY, DXSIZE, DYSIZE, 0, 0);
4040
4041     if (old_door_state & DOOR_OPEN_2)
4042       BlitBitmap(backbuffer, bitmap_db_door_2,
4043                  VX, VY, VXSIZE, VYSIZE, 0, 0);
4044
4045     door_state &= ~DOOR_NO_COPY_BACK;
4046   }
4047
4048   return MoveDoor(door_state);
4049 }
4050
4051 unsigned int GetDoorState()
4052 {
4053   return MoveDoor(DOOR_GET_STATE);
4054 }
4055
4056 unsigned int SetDoorState(unsigned int door_state)
4057 {
4058   return MoveDoor(door_state | DOOR_SET_STATE);
4059 }
4060
4061 int euclid(int a, int b)
4062 {
4063   return (b ? euclid(b, a % b) : a);
4064 }
4065
4066 unsigned int MoveDoor(unsigned int door_state)
4067 {
4068   struct Rect door_rect_list[] =
4069   {
4070     { DX, DY, DXSIZE, DYSIZE },
4071     { VX, VY, VXSIZE, VYSIZE }
4072   };
4073   static int door1 = DOOR_OPEN_1;
4074   static int door2 = DOOR_CLOSE_2;
4075   unsigned int door_delay = 0;
4076   unsigned int door_delay_value;
4077   int i;
4078
4079   if (door_state == DOOR_GET_STATE)
4080     return (door1 | door2);
4081
4082   if (door_state & DOOR_SET_STATE)
4083   {
4084     if (door_state & DOOR_ACTION_1)
4085       door1 = door_state & DOOR_ACTION_1;
4086     if (door_state & DOOR_ACTION_2)
4087       door2 = door_state & DOOR_ACTION_2;
4088
4089     return (door1 | door2);
4090   }
4091
4092   if (!(door_state & DOOR_FORCE_REDRAW))
4093   {
4094     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4095       door_state &= ~DOOR_OPEN_1;
4096     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4097       door_state &= ~DOOR_CLOSE_1;
4098     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4099       door_state &= ~DOOR_OPEN_2;
4100     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4101       door_state &= ~DOOR_CLOSE_2;
4102   }
4103
4104   if (global.autoplay_leveldir)
4105   {
4106     door_state |= DOOR_NO_DELAY;
4107     door_state &= ~DOOR_CLOSE_ALL;
4108   }
4109
4110   if (game_status == GAME_MODE_EDITOR)
4111     door_state |= DOOR_NO_DELAY;
4112
4113   if (door_state & DOOR_ACTION)
4114   {
4115     boolean door_panel_drawn[NUM_DOORS];
4116     boolean panel_has_doors[NUM_DOORS];
4117     boolean door_part_skip[MAX_DOOR_PARTS];
4118     boolean door_part_done[MAX_DOOR_PARTS];
4119     boolean door_part_done_all;
4120     int num_steps[MAX_DOOR_PARTS];
4121     int max_move_delay = 0;     // delay for complete animations of all doors
4122     int max_step_delay = 0;     // delay (ms) between two animation frames
4123     int num_move_steps = 0;     // number of animation steps for all doors
4124     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
4125     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
4126     int current_move_delay = 0;
4127     int start = 0;
4128     int k;
4129
4130     for (i = 0; i < NUM_DOORS; i++)
4131       panel_has_doors[i] = FALSE;
4132
4133     for (i = 0; i < MAX_DOOR_PARTS; i++)
4134     {
4135       struct DoorPartControlInfo *dpc = &door_part_controls[i];
4136       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4137       int door_token = dpc->door_token;
4138
4139       door_part_done[i] = FALSE;
4140       door_part_skip[i] = (!(door_state & door_token) ||
4141                            !g->bitmap);
4142     }
4143
4144     for (i = 0; i < MAX_DOOR_PARTS; i++)
4145     {
4146       int nr = door_part_order[i].nr;
4147       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4148       struct DoorPartPosInfo *pos = dpc->pos;
4149       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4150       int door_token = dpc->door_token;
4151       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4152       boolean is_panel = DOOR_PART_IS_PANEL(nr);
4153       int step_xoffset = ABS(pos->step_xoffset);
4154       int step_yoffset = ABS(pos->step_yoffset);
4155       int step_delay = pos->step_delay;
4156       int current_door_state = door_state & door_token;
4157       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4158       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4159       boolean part_opening = (is_panel ? door_closing : door_opening);
4160       int start_step = (part_opening ? pos->start_step_opening :
4161                         pos->start_step_closing);
4162       float move_xsize = (step_xoffset ? g->width  : 0);
4163       float move_ysize = (step_yoffset ? g->height : 0);
4164       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4165       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4166       int move_steps = (move_xsteps && move_ysteps ?
4167                         MIN(move_xsteps, move_ysteps) :
4168                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
4169       int move_delay = move_steps * step_delay;
4170
4171       if (door_part_skip[nr])
4172         continue;
4173
4174       max_move_delay = MAX(max_move_delay, move_delay);
4175       max_step_delay = (max_step_delay == 0 ? step_delay :
4176                         euclid(max_step_delay, step_delay));
4177       num_steps[nr] = move_steps;
4178
4179       if (!is_panel)
4180       {
4181         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4182
4183         panel_has_doors[door_index] = TRUE;
4184       }
4185     }
4186
4187     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
4188
4189     num_move_steps = max_move_delay / max_step_delay;
4190     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4191
4192     door_delay_value = max_step_delay;
4193
4194     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4195     {
4196       start = num_move_steps - 1;
4197     }
4198     else
4199     {
4200       /* opening door sound has priority over simultaneously closing door */
4201       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4202         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4203       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4204         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4205     }
4206
4207     for (k = start; k < num_move_steps; k++)
4208     {
4209       door_part_done_all = TRUE;
4210
4211       for (i = 0; i < NUM_DOORS; i++)
4212         door_panel_drawn[i] = FALSE;
4213
4214       for (i = 0; i < MAX_DOOR_PARTS; i++)
4215       {
4216         int nr = door_part_order[i].nr;
4217         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4218         struct DoorPartPosInfo *pos = dpc->pos;
4219         struct GraphicInfo *g = &graphic_info[dpc->graphic];
4220         int door_token = dpc->door_token;
4221         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4222         boolean is_panel = DOOR_PART_IS_PANEL(nr);
4223         boolean is_panel_and_door_has_closed = FALSE;
4224         struct Rect *door_rect = &door_rect_list[door_index];
4225         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4226                                   bitmap_db_door_2);
4227         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4228         int current_door_state = door_state & door_token;
4229         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4230         boolean door_closing = !door_opening;
4231         boolean part_opening = (is_panel ? door_closing : door_opening);
4232         boolean part_closing = !part_opening;
4233         int start_step = (part_opening ? pos->start_step_opening :
4234                           pos->start_step_closing);
4235         int step_delay = pos->step_delay;
4236         int step_factor = step_delay / max_step_delay;
4237         int k1 = (step_factor ? k / step_factor + 1 : k);
4238         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4239         int kk = MAX(0, k2);
4240         int g_src_x = 0;
4241         int g_src_y = 0;
4242         int src_x, src_y, src_xx, src_yy;
4243         int dst_x, dst_y, dst_xx, dst_yy;
4244         int width, height;
4245
4246         if (door_part_skip[nr])
4247           continue;
4248
4249         if (!(door_state & door_token))
4250           continue;
4251
4252         if (!g->bitmap)
4253           continue;
4254
4255         if (!is_panel)
4256         {
4257           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4258           int kk_door = MAX(0, k2_door);
4259           int sync_frame = kk_door * door_delay_value;
4260           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4261
4262           getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4263         }
4264
4265         // draw door panel
4266
4267         if (!door_panel_drawn[door_index])
4268         {
4269           ClearRectangle(drawto, door_rect->x, door_rect->y,
4270                          door_rect->width, door_rect->height);
4271
4272           door_panel_drawn[door_index] = TRUE;
4273         }
4274
4275         // draw opening or closing door parts
4276
4277         if (pos->step_xoffset < 0)      // door part on right side
4278         {
4279           src_xx = 0;
4280           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4281           width = g->width;
4282
4283           if (dst_xx + width > door_rect->width)
4284             width = door_rect->width - dst_xx;
4285         }
4286         else                            // door part on left side
4287         {
4288           src_xx = 0;
4289           dst_xx = pos->x - kk * pos->step_xoffset;
4290
4291           if (dst_xx < 0)
4292           {
4293             src_xx = ABS(dst_xx);
4294             dst_xx = 0;
4295           }
4296
4297           width = g->width - src_xx;
4298
4299           // printf("::: k == %d [%d] \n", k, start_step);
4300         }
4301
4302         if (pos->step_yoffset < 0)      // door part on bottom side
4303         {
4304           src_yy = 0;
4305           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4306           height = g->height;
4307
4308           if (dst_yy + height > door_rect->height)
4309             height = door_rect->height - dst_yy;
4310         }
4311         else                            // door part on top side
4312         {
4313           src_yy = 0;
4314           dst_yy = pos->y - kk * pos->step_yoffset;
4315
4316           if (dst_yy < 0)
4317           {
4318             src_yy = ABS(dst_yy);
4319             dst_yy = 0;
4320           }
4321
4322           height = g->height - src_yy;
4323         }
4324
4325         src_x = g_src_x + src_xx;
4326         src_y = g_src_y + src_yy;
4327
4328         dst_x = door_rect->x + dst_xx;
4329         dst_y = door_rect->y + dst_yy;
4330
4331         is_panel_and_door_has_closed =
4332           (is_panel &&
4333            door_closing &&
4334            panel_has_doors[door_index] &&
4335            k >= num_move_steps_doors_only - 1);
4336
4337         if (width  >= 0 && width  <= g->width &&
4338             height >= 0 && height <= g->height &&
4339             !is_panel_and_door_has_closed)
4340         {
4341           if (is_panel || !pos->draw_masked)
4342             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4343                        dst_x, dst_y);
4344           else
4345             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4346                              dst_x, dst_y);
4347         }
4348
4349         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4350
4351         if ((part_opening && (width < 0         || height < 0)) ||
4352             (part_closing && (width >= g->width && height >= g->height)))
4353           door_part_done[nr] = TRUE;
4354
4355         // continue door part animations, but not panel after door has closed
4356         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4357           door_part_done_all = FALSE;
4358       }
4359
4360       if (!(door_state & DOOR_NO_DELAY))
4361       {
4362         BackToFront();
4363
4364         if (game_status == GAME_MODE_MAIN)
4365           DoAnimation();
4366
4367         WaitUntilDelayReached(&door_delay, door_delay_value);
4368
4369         current_move_delay += max_step_delay;
4370       }
4371
4372       if (door_part_done_all)
4373         break;
4374     }
4375   }
4376
4377   if (door_state & DOOR_ACTION_1)
4378     door1 = door_state & DOOR_ACTION_1;
4379   if (door_state & DOOR_ACTION_2)
4380     door2 = door_state & DOOR_ACTION_2;
4381
4382   return (door1 | door2);
4383 }
4384
4385 void DrawSpecialEditorDoor()
4386 {
4387   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4388   int top_border_width = gfx1->width;
4389   int top_border_height = gfx1->height;
4390   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4391   int ex = EX - outer_border;
4392   int ey = EY - outer_border;
4393   int vy = VY - outer_border;
4394   int exsize = EXSIZE + 2 * outer_border;
4395
4396   /* draw bigger level editor toolbox window */
4397   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4398              top_border_width, top_border_height, ex, ey - top_border_height);
4399   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4400              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4401
4402   redraw_mask |= REDRAW_ALL;
4403 }
4404
4405 void UndrawSpecialEditorDoor()
4406 {
4407   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4408   int top_border_width = gfx1->width;
4409   int top_border_height = gfx1->height;
4410   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4411   int ex = EX - outer_border;
4412   int ey = EY - outer_border;
4413   int ey_top = ey - top_border_height;
4414   int exsize = EXSIZE + 2 * outer_border;
4415   int eysize = EYSIZE + 2 * outer_border;
4416
4417   /* draw normal tape recorder window */
4418   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4419   {
4420     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4421                ex, ey_top, top_border_width, top_border_height,
4422                ex, ey_top);
4423     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4424                ex, ey, exsize, eysize, ex, ey);
4425   }
4426   else
4427   {
4428     // if screen background is set to "[NONE]", clear editor toolbox window
4429     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4430     ClearRectangle(drawto, ex, ey, exsize, eysize);
4431   }
4432
4433   redraw_mask |= REDRAW_ALL;
4434 }
4435
4436
4437 /* ---------- new tool button stuff ---------------------------------------- */
4438
4439 static struct
4440 {
4441   int graphic;
4442   struct TextPosInfo *pos;
4443   int gadget_id;
4444   char *infotext;
4445 } toolbutton_info[NUM_TOOL_BUTTONS] =
4446 {
4447   {
4448     IMG_REQUEST_BUTTON_GFX_YES,         &request.button.yes,
4449     TOOL_CTRL_ID_YES,                   "yes"
4450   },
4451   {
4452     IMG_REQUEST_BUTTON_GFX_NO,          &request.button.no,
4453     TOOL_CTRL_ID_NO,                    "no"
4454   },
4455   {
4456     IMG_REQUEST_BUTTON_GFX_CONFIRM,     &request.button.confirm,
4457     TOOL_CTRL_ID_CONFIRM,               "confirm"
4458   },
4459   {
4460     IMG_REQUEST_BUTTON_GFX_PLAYER_1,    &request.button.player_1,
4461     TOOL_CTRL_ID_PLAYER_1,              "player 1"
4462   },
4463   {
4464     IMG_REQUEST_BUTTON_GFX_PLAYER_2,    &request.button.player_2,
4465     TOOL_CTRL_ID_PLAYER_2,              "player 2"
4466   },
4467   {
4468     IMG_REQUEST_BUTTON_GFX_PLAYER_3,    &request.button.player_3,
4469     TOOL_CTRL_ID_PLAYER_3,              "player 3"
4470   },
4471   {
4472     IMG_REQUEST_BUTTON_GFX_PLAYER_4,    &request.button.player_4,
4473     TOOL_CTRL_ID_PLAYER_4,              "player 4"
4474   }
4475 };
4476
4477 void CreateToolButtons()
4478 {
4479   int i;
4480
4481   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4482   {
4483     struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4484     struct TextPosInfo *pos = toolbutton_info[i].pos;
4485     struct GadgetInfo *gi;
4486     Bitmap *deco_bitmap = None;
4487     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4488     unsigned int event_mask = GD_EVENT_RELEASED;
4489     int dx = DX;
4490     int dy = DY;
4491     int gd_x = gfx->src_x;
4492     int gd_y = gfx->src_y;
4493     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4494     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4495     int id = i;
4496
4497     if (global.use_envelope_request)
4498       setRequestPosition(&dx, &dy, TRUE);
4499
4500     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4501     {
4502       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4503
4504       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4505                             pos->size, &deco_bitmap, &deco_x, &deco_y);
4506       deco_xpos = (gfx->width  - pos->size) / 2;
4507       deco_ypos = (gfx->height - pos->size) / 2;
4508     }
4509
4510     gi = CreateGadget(GDI_CUSTOM_ID, id,
4511                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
4512                       GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4513                       GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4514                       GDI_WIDTH, gfx->width,
4515                       GDI_HEIGHT, gfx->height,
4516                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4517                       GDI_STATE, GD_BUTTON_UNPRESSED,
4518                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4519                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4520                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4521                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4522                       GDI_DECORATION_SIZE, pos->size, pos->size,
4523                       GDI_DECORATION_SHIFTING, 1, 1,
4524                       GDI_DIRECT_DRAW, FALSE,
4525                       GDI_EVENT_MASK, event_mask,
4526                       GDI_CALLBACK_ACTION, HandleToolButtons,
4527                       GDI_END);
4528
4529     if (gi == NULL)
4530       Error(ERR_EXIT, "cannot create gadget");
4531
4532     tool_gadget[id] = gi;
4533   }
4534 }
4535
4536 void FreeToolButtons()
4537 {
4538   int i;
4539
4540   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4541     FreeGadget(tool_gadget[i]);
4542 }
4543
4544 static void UnmapToolButtons()
4545 {
4546   int i;
4547
4548   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4549     UnmapGadget(tool_gadget[i]);
4550 }
4551
4552 static void HandleToolButtons(struct GadgetInfo *gi)
4553 {
4554   request_gadget_id = gi->custom_id;
4555 }
4556
4557 static struct Mapping_EM_to_RND_object
4558 {
4559   int element_em;
4560   boolean is_rnd_to_em_mapping;         /* unique mapping EM <-> RND */
4561   boolean is_backside;                  /* backside of moving element */
4562
4563   int element_rnd;
4564   int action;
4565   int direction;
4566 }
4567 em_object_mapping_list[] =
4568 {
4569   {
4570     Xblank,                             TRUE,   FALSE,
4571     EL_EMPTY,                           -1, -1
4572   },
4573   {
4574     Yacid_splash_eB,                    FALSE,  FALSE,
4575     EL_ACID_SPLASH_RIGHT,               -1, -1
4576   },
4577   {
4578     Yacid_splash_wB,                    FALSE,  FALSE,
4579     EL_ACID_SPLASH_LEFT,                -1, -1
4580   },
4581
4582 #ifdef EM_ENGINE_BAD_ROLL
4583   {
4584     Xstone_force_e,                     FALSE,  FALSE,
4585     EL_ROCK,                            -1, MV_BIT_RIGHT
4586   },
4587   {
4588     Xstone_force_w,                     FALSE,  FALSE,
4589     EL_ROCK,                            -1, MV_BIT_LEFT
4590   },
4591   {
4592     Xnut_force_e,                       FALSE,  FALSE,
4593     EL_NUT,                             -1, MV_BIT_RIGHT
4594   },
4595   {
4596     Xnut_force_w,                       FALSE,  FALSE,
4597     EL_NUT,                             -1, MV_BIT_LEFT
4598   },
4599   {
4600     Xspring_force_e,                    FALSE,  FALSE,
4601     EL_SPRING,                          -1, MV_BIT_RIGHT
4602   },
4603   {
4604     Xspring_force_w,                    FALSE,  FALSE,
4605     EL_SPRING,                          -1, MV_BIT_LEFT
4606   },
4607   {
4608     Xemerald_force_e,                   FALSE,  FALSE,
4609     EL_EMERALD,                         -1, MV_BIT_RIGHT
4610   },
4611   {
4612     Xemerald_force_w,                   FALSE,  FALSE,
4613     EL_EMERALD,                         -1, MV_BIT_LEFT
4614   },
4615   {
4616     Xdiamond_force_e,                   FALSE,  FALSE,
4617     EL_DIAMOND,                         -1, MV_BIT_RIGHT
4618   },
4619   {
4620     Xdiamond_force_w,                   FALSE,  FALSE,
4621     EL_DIAMOND,                         -1, MV_BIT_LEFT
4622   },
4623   {
4624     Xbomb_force_e,                      FALSE,  FALSE,
4625     EL_BOMB,                            -1, MV_BIT_RIGHT
4626   },
4627   {
4628     Xbomb_force_w,                      FALSE,  FALSE,
4629     EL_BOMB,                            -1, MV_BIT_LEFT
4630   },
4631 #endif  /* EM_ENGINE_BAD_ROLL */
4632
4633   {
4634     Xstone,                             TRUE,   FALSE,
4635     EL_ROCK,                            -1, -1
4636   },
4637   {
4638     Xstone_pause,                       FALSE,  FALSE,
4639     EL_ROCK,                            -1, -1
4640   },
4641   {
4642     Xstone_fall,                        FALSE,  FALSE,
4643     EL_ROCK,                            -1, -1
4644   },
4645   {
4646     Ystone_s,                           FALSE,  FALSE,
4647     EL_ROCK,                            ACTION_FALLING, -1
4648   },
4649   {
4650     Ystone_sB,                          FALSE,  TRUE,
4651     EL_ROCK,                            ACTION_FALLING, -1
4652   },
4653   {
4654     Ystone_e,                           FALSE,  FALSE,
4655     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
4656   },
4657   {
4658     Ystone_eB,                          FALSE,  TRUE,
4659     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
4660   },
4661   {
4662     Ystone_w,                           FALSE,  FALSE,
4663     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
4664   },
4665   {
4666     Ystone_wB,                          FALSE,  TRUE,
4667     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
4668   },
4669   {
4670     Xnut,                               TRUE,   FALSE,
4671     EL_NUT,                             -1, -1
4672   },
4673   {
4674     Xnut_pause,                         FALSE,  FALSE,
4675     EL_NUT,                             -1, -1
4676   },
4677   {
4678     Xnut_fall,                          FALSE,  FALSE,
4679     EL_NUT,                             -1, -1
4680   },
4681   {
4682     Ynut_s,                             FALSE,  FALSE,
4683     EL_NUT,                             ACTION_FALLING, -1
4684   },
4685   {
4686     Ynut_sB,                            FALSE,  TRUE,
4687     EL_NUT,                             ACTION_FALLING, -1
4688   },
4689   {
4690     Ynut_e,                             FALSE,  FALSE,
4691     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
4692   },
4693   {
4694     Ynut_eB,                            FALSE,  TRUE,
4695     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
4696   },
4697   {
4698     Ynut_w,                             FALSE,  FALSE,
4699     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
4700   },
4701   {
4702     Ynut_wB,                            FALSE,  TRUE,
4703     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
4704   },
4705   {
4706     Xbug_n,                             TRUE,   FALSE,
4707     EL_BUG_UP,                          -1, -1
4708   },
4709   {
4710     Xbug_e,                             TRUE,   FALSE,
4711     EL_BUG_RIGHT,                       -1, -1
4712   },
4713   {
4714     Xbug_s,                             TRUE,   FALSE,
4715     EL_BUG_DOWN,                        -1, -1
4716   },
4717   {
4718     Xbug_w,                             TRUE,   FALSE,
4719     EL_BUG_LEFT,                        -1, -1
4720   },
4721   {
4722     Xbug_gon,                           FALSE,  FALSE,
4723     EL_BUG_UP,                          -1, -1
4724   },
4725   {
4726     Xbug_goe,                           FALSE,  FALSE,
4727     EL_BUG_RIGHT,                       -1, -1
4728   },
4729   {
4730     Xbug_gos,                           FALSE,  FALSE,
4731     EL_BUG_DOWN,                        -1, -1
4732   },
4733   {
4734     Xbug_gow,                           FALSE,  FALSE,
4735     EL_BUG_LEFT,                        -1, -1
4736   },
4737   {
4738     Ybug_n,                             FALSE,  FALSE,
4739     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
4740   },
4741   {
4742     Ybug_nB,                            FALSE,  TRUE,
4743     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
4744   },
4745   {
4746     Ybug_e,                             FALSE,  FALSE,
4747     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
4748   },
4749   {
4750     Ybug_eB,                            FALSE,  TRUE,
4751     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
4752   },
4753   {
4754     Ybug_s,                             FALSE,  FALSE,
4755     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
4756   },
4757   {
4758     Ybug_sB,                            FALSE,  TRUE,
4759     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
4760   },
4761   {
4762     Ybug_w,                             FALSE,  FALSE,
4763     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
4764   },
4765   {
4766     Ybug_wB,                            FALSE,  TRUE,
4767     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
4768   },
4769   {
4770     Ybug_w_n,                           FALSE,  FALSE,
4771     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4772   },
4773   {
4774     Ybug_n_e,                           FALSE,  FALSE,
4775     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4776   },
4777   {
4778     Ybug_e_s,                           FALSE,  FALSE,
4779     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4780   },
4781   {
4782     Ybug_s_w,                           FALSE,  FALSE,
4783     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4784   },
4785   {
4786     Ybug_e_n,                           FALSE,  FALSE,
4787     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4788   },
4789   {
4790     Ybug_s_e,                           FALSE,  FALSE,
4791     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4792   },
4793   {
4794     Ybug_w_s,                           FALSE,  FALSE,
4795     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4796   },
4797   {
4798     Ybug_n_w,                           FALSE,  FALSE,
4799     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4800   },
4801   {
4802     Ybug_stone,                         FALSE,  FALSE,
4803     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
4804   },
4805   {
4806     Ybug_spring,                        FALSE,  FALSE,
4807     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
4808   },
4809   {
4810     Xtank_n,                            TRUE,   FALSE,
4811     EL_SPACESHIP_UP,                    -1, -1
4812   },
4813   {
4814     Xtank_e,                            TRUE,   FALSE,
4815     EL_SPACESHIP_RIGHT,                 -1, -1
4816   },
4817   {
4818     Xtank_s,                            TRUE,   FALSE,
4819     EL_SPACESHIP_DOWN,                  -1, -1
4820   },
4821   {
4822     Xtank_w,                            TRUE,   FALSE,
4823     EL_SPACESHIP_LEFT,                  -1, -1
4824   },
4825   {
4826     Xtank_gon,                          FALSE,  FALSE,
4827     EL_SPACESHIP_UP,                    -1, -1
4828   },
4829   {
4830     Xtank_goe,                          FALSE,  FALSE,
4831     EL_SPACESHIP_RIGHT,                 -1, -1
4832   },
4833   {
4834     Xtank_gos,                          FALSE,  FALSE,
4835     EL_SPACESHIP_DOWN,                  -1, -1
4836   },
4837   {
4838     Xtank_gow,                          FALSE,  FALSE,
4839     EL_SPACESHIP_LEFT,                  -1, -1
4840   },
4841   {
4842     Ytank_n,                            FALSE,  FALSE,
4843     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
4844   },
4845   {
4846     Ytank_nB,                           FALSE,  TRUE,
4847     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
4848   },
4849   {
4850     Ytank_e,                            FALSE,  FALSE,
4851     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
4852   },
4853   {
4854     Ytank_eB,                           FALSE,  TRUE,
4855     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
4856   },
4857   {
4858     Ytank_s,                            FALSE,  FALSE,
4859     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
4860   },
4861   {
4862     Ytank_sB,                           FALSE,  TRUE,
4863     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
4864   },
4865   {
4866     Ytank_w,                            FALSE,  FALSE,
4867     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
4868   },
4869   {
4870     Ytank_wB,                           FALSE,  TRUE,
4871     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
4872   },
4873   {
4874     Ytank_w_n,                          FALSE,  FALSE,
4875     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4876   },
4877   {
4878     Ytank_n_e,                          FALSE,  FALSE,
4879     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4880   },
4881   {
4882     Ytank_e_s,                          FALSE,  FALSE,
4883     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4884   },
4885   {
4886     Ytank_s_w,                          FALSE,  FALSE,
4887     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4888   },
4889   {
4890     Ytank_e_n,                          FALSE,  FALSE,
4891     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4892   },
4893   {
4894     Ytank_s_e,                          FALSE,  FALSE,
4895     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4896   },
4897   {
4898     Ytank_w_s,                          FALSE,  FALSE,
4899     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4900   },
4901   {
4902     Ytank_n_w,                          FALSE,  FALSE,
4903     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4904   },
4905   {
4906     Ytank_stone,                        FALSE,  FALSE,
4907     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
4908   },
4909   {
4910     Ytank_spring,                       FALSE,  FALSE,
4911     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
4912   },
4913   {
4914     Xandroid,                           TRUE,   FALSE,
4915     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
4916   },
4917   {
4918     Xandroid_1_n,                       FALSE,  FALSE,
4919     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
4920   },
4921   {
4922     Xandroid_2_n,                       FALSE,  FALSE,
4923     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
4924   },
4925   {
4926     Xandroid_1_e,                       FALSE,  FALSE,
4927     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
4928   },
4929   {
4930     Xandroid_2_e,                       FALSE,  FALSE,
4931     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
4932   },
4933   {
4934     Xandroid_1_w,                       FALSE,  FALSE,
4935     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
4936   },
4937   {
4938     Xandroid_2_w,                       FALSE,  FALSE,
4939     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
4940   },
4941   {
4942     Xandroid_1_s,                       FALSE,  FALSE,
4943     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
4944   },
4945   {
4946     Xandroid_2_s,                       FALSE,  FALSE,
4947     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
4948   },
4949   {
4950     Yandroid_n,                         FALSE,  FALSE,
4951     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
4952   },
4953   {
4954     Yandroid_nB,                        FALSE,  TRUE,
4955     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
4956   },
4957   {
4958     Yandroid_ne,                        FALSE,  FALSE,
4959     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
4960   },
4961   {
4962     Yandroid_neB,                       FALSE,  TRUE,
4963     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
4964   },
4965   {
4966     Yandroid_e,                         FALSE,  FALSE,
4967     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
4968   },
4969   {
4970     Yandroid_eB,                        FALSE,  TRUE,
4971     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
4972   },
4973   {
4974     Yandroid_se,                        FALSE,  FALSE,
4975     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
4976   },
4977   {
4978     Yandroid_seB,                       FALSE,  TRUE,
4979     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4980   },
4981   {
4982     Yandroid_s,                         FALSE,  FALSE,
4983     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
4984   },
4985   {
4986     Yandroid_sB,                        FALSE,  TRUE,
4987     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
4988   },
4989   {
4990     Yandroid_sw,                        FALSE,  FALSE,
4991     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
4992   },
4993   {
4994     Yandroid_swB,                       FALSE,  TRUE,
4995     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
4996   },
4997   {
4998     Yandroid_w,                         FALSE,  FALSE,
4999     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5000   },
5001   {
5002     Yandroid_wB,                        FALSE,  TRUE,
5003     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5004   },
5005   {
5006     Yandroid_nw,                        FALSE,  FALSE,
5007     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
5008   },
5009   {
5010     Yandroid_nwB,                       FALSE,  TRUE,
5011     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
5012   },
5013   {
5014     Xspring,                            TRUE,   FALSE,
5015     EL_SPRING,                          -1, -1
5016   },
5017   {
5018     Xspring_pause,                      FALSE,  FALSE,
5019     EL_SPRING,                          -1, -1
5020   },
5021   {
5022     Xspring_e,                          FALSE,  FALSE,
5023     EL_SPRING,                          -1, -1
5024   },
5025   {
5026     Xspring_w,                          FALSE,  FALSE,
5027     EL_SPRING,                          -1, -1
5028   },
5029   {
5030     Xspring_fall,                       FALSE,  FALSE,
5031     EL_SPRING,                          -1, -1
5032   },
5033   {
5034     Yspring_s,                          FALSE,  FALSE,
5035     EL_SPRING,                          ACTION_FALLING, -1
5036   },
5037   {
5038     Yspring_sB,                         FALSE,  TRUE,
5039     EL_SPRING,                          ACTION_FALLING, -1
5040   },
5041   {
5042     Yspring_e,                          FALSE,  FALSE,
5043     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5044   },
5045   {
5046     Yspring_eB,                         FALSE,  TRUE,
5047     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5048   },
5049   {
5050     Yspring_w,                          FALSE,  FALSE,
5051     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5052   },
5053   {
5054     Yspring_wB,                         FALSE,  TRUE,
5055     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5056   },
5057   {
5058     Yspring_kill_e,                     FALSE,  FALSE,
5059     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5060   },
5061   {
5062     Yspring_kill_eB,                    FALSE,  TRUE,
5063     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5064   },
5065   {
5066     Yspring_kill_w,                     FALSE,  FALSE,
5067     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5068   },
5069   {
5070     Yspring_kill_wB,                    FALSE,  TRUE,
5071     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5072   },
5073   {
5074     Xeater_n,                           TRUE,   FALSE,
5075     EL_YAMYAM_UP,                       -1, -1
5076   },
5077   {
5078     Xeater_e,                           TRUE,   FALSE,
5079     EL_YAMYAM_RIGHT,                    -1, -1
5080   },
5081   {
5082     Xeater_w,                           TRUE,   FALSE,
5083     EL_YAMYAM_LEFT,                     -1, -1
5084   },
5085   {
5086     Xeater_s,                           TRUE,   FALSE,
5087     EL_YAMYAM_DOWN,                     -1, -1
5088   },
5089   {
5090     Yeater_n,                           FALSE,  FALSE,
5091     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5092   },
5093   {
5094     Yeater_nB,                          FALSE,  TRUE,
5095     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5096   },
5097   {
5098     Yeater_e,                           FALSE,  FALSE,
5099     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5100   },
5101   {
5102     Yeater_eB,                          FALSE,  TRUE,
5103     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5104   },
5105   {
5106     Yeater_s,                           FALSE,  FALSE,
5107     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5108   },
5109   {
5110     Yeater_sB,                          FALSE,  TRUE,
5111     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5112   },
5113   {
5114     Yeater_w,                           FALSE,  FALSE,
5115     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5116   },
5117   {
5118     Yeater_wB,                          FALSE,  TRUE,
5119     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5120   },
5121   {
5122     Yeater_stone,                       FALSE,  FALSE,
5123     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
5124   },
5125   {
5126     Yeater_spring,                      FALSE,  FALSE,
5127     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
5128   },
5129   {
5130     Xalien,                             TRUE,   FALSE,
5131     EL_ROBOT,                           -1, -1
5132   },
5133   {
5134     Xalien_pause,                       FALSE,  FALSE,
5135     EL_ROBOT,                           -1, -1
5136   },
5137   {
5138     Yalien_n,                           FALSE,  FALSE,
5139     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5140   },
5141   {
5142     Yalien_nB,                          FALSE,  TRUE,
5143     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5144   },
5145   {
5146     Yalien_e,                           FALSE,  FALSE,
5147     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5148   },
5149   {
5150     Yalien_eB,                          FALSE,  TRUE,
5151     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5152   },
5153   {
5154     Yalien_s,                           FALSE,  FALSE,
5155     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5156   },
5157   {
5158     Yalien_sB,                          FALSE,  TRUE,
5159     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5160   },
5161   {
5162     Yalien_w,                           FALSE,  FALSE,
5163     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5164   },
5165   {
5166     Yalien_wB,                          FALSE,  TRUE,
5167     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5168   },
5169   {
5170     Yalien_stone,                       FALSE,  FALSE,
5171     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
5172   },
5173   {
5174     Yalien_spring,                      FALSE,  FALSE,
5175     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
5176   },
5177   {
5178     Xemerald,                           TRUE,   FALSE,
5179     EL_EMERALD,                         -1, -1
5180   },
5181   {
5182     Xemerald_pause,                     FALSE,  FALSE,
5183     EL_EMERALD,                         -1, -1
5184   },
5185   {
5186     Xemerald_fall,                      FALSE,  FALSE,
5187     EL_EMERALD,                         -1, -1
5188   },
5189   {
5190     Xemerald_shine,                     FALSE,  FALSE,
5191     EL_EMERALD,                         ACTION_TWINKLING, -1
5192   },
5193   {
5194     Yemerald_s,                         FALSE,  FALSE,
5195     EL_EMERALD,                         ACTION_FALLING, -1
5196   },
5197   {
5198     Yemerald_sB,                        FALSE,  TRUE,
5199     EL_EMERALD,                         ACTION_FALLING, -1
5200   },
5201   {
5202     Yemerald_e,                         FALSE,  FALSE,
5203     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5204   },
5205   {
5206     Yemerald_eB,                        FALSE,  TRUE,
5207     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5208   },
5209   {
5210     Yemerald_w,                         FALSE,  FALSE,
5211     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5212   },
5213   {
5214     Yemerald_wB,                        FALSE,  TRUE,
5215     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5216   },
5217   {
5218     Yemerald_eat,                       FALSE,  FALSE,
5219     EL_EMERALD,                         ACTION_COLLECTING, -1
5220   },
5221   {
5222     Yemerald_stone,                     FALSE,  FALSE,
5223     EL_NUT,                             ACTION_BREAKING, -1
5224   },
5225   {
5226     Xdiamond,                           TRUE,   FALSE,
5227     EL_DIAMOND,                         -1, -1
5228   },
5229   {
5230     Xdiamond_pause,                     FALSE,  FALSE,
5231     EL_DIAMOND,                         -1, -1
5232   },
5233   {
5234     Xdiamond_fall,                      FALSE,  FALSE,
5235     EL_DIAMOND,                         -1, -1
5236   },
5237   {
5238     Xdiamond_shine,                     FALSE,  FALSE,
5239     EL_DIAMOND,                         ACTION_TWINKLING, -1
5240   },
5241   {
5242     Ydiamond_s,                         FALSE,  FALSE,
5243     EL_DIAMOND,                         ACTION_FALLING, -1
5244   },
5245   {
5246     Ydiamond_sB,                        FALSE,  TRUE,
5247     EL_DIAMOND,                         ACTION_FALLING, -1
5248   },
5249   {
5250     Ydiamond_e,                         FALSE,  FALSE,
5251     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5252   },
5253   {
5254     Ydiamond_eB,                        FALSE,  TRUE,
5255     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5256   },
5257   {
5258     Ydiamond_w,                         FALSE,  FALSE,
5259     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5260   },
5261   {
5262     Ydiamond_wB,                        FALSE,  TRUE,
5263     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5264   },
5265   {
5266     Ydiamond_eat,                       FALSE,  FALSE,
5267     EL_DIAMOND,                         ACTION_COLLECTING, -1
5268   },
5269   {
5270     Ydiamond_stone,                     FALSE,  FALSE,
5271     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
5272   },
5273   {
5274     Xdrip_fall,                         TRUE,   FALSE,
5275     EL_AMOEBA_DROP,                     -1, -1
5276   },
5277   {
5278     Xdrip_stretch,                      FALSE,  FALSE,
5279     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5280   },
5281   {
5282     Xdrip_stretchB,                     FALSE,  TRUE,
5283     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5284   },
5285   {
5286     Xdrip_eat,                          FALSE,  FALSE,
5287     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
5288   },
5289   {
5290     Ydrip_s1,                           FALSE,  FALSE,
5291     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5292   },
5293   {
5294     Ydrip_s1B,                          FALSE,  TRUE,
5295     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5296   },
5297   {
5298     Ydrip_s2,                           FALSE,  FALSE,
5299     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5300   },
5301   {
5302     Ydrip_s2B,                          FALSE,  TRUE,
5303     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5304   },
5305   {
5306     Xbomb,                              TRUE,   FALSE,
5307     EL_BOMB,                            -1, -1
5308   },
5309   {
5310     Xbomb_pause,                        FALSE,  FALSE,
5311     EL_BOMB,                            -1, -1
5312   },
5313   {
5314     Xbomb_fall,                         FALSE,  FALSE,
5315     EL_BOMB,                            -1, -1
5316   },
5317   {
5318     Ybomb_s,                            FALSE,  FALSE,
5319     EL_BOMB,                            ACTION_FALLING, -1
5320   },
5321   {
5322     Ybomb_sB,                           FALSE,  TRUE,
5323     EL_BOMB,                            ACTION_FALLING, -1
5324   },
5325   {
5326     Ybomb_e,                            FALSE,  FALSE,
5327     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5328   },
5329   {
5330     Ybomb_eB,                           FALSE,  TRUE,
5331     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5332   },
5333   {
5334     Ybomb_w,                            FALSE,  FALSE,
5335     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5336   },
5337   {
5338     Ybomb_wB,                           FALSE,  TRUE,
5339     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5340   },
5341   {
5342     Ybomb_eat,                          FALSE,  FALSE,
5343     EL_BOMB,                            ACTION_ACTIVATING, -1
5344   },
5345   {
5346     Xballoon,                           TRUE,   FALSE,
5347     EL_BALLOON,                         -1, -1
5348   },
5349   {
5350     Yballoon_n,                         FALSE,  FALSE,
5351     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5352   },
5353   {
5354     Yballoon_nB,                        FALSE,  TRUE,
5355     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5356   },
5357   {
5358     Yballoon_e,                         FALSE,  FALSE,
5359     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5360   },
5361   {
5362     Yballoon_eB,                        FALSE,  TRUE,
5363     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5364   },
5365   {
5366     Yballoon_s,                         FALSE,  FALSE,
5367     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5368   },
5369   {
5370     Yballoon_sB,                        FALSE,  TRUE,
5371     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5372   },
5373   {
5374     Yballoon_w,                         FALSE,  FALSE,
5375     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5376   },
5377   {
5378     Yballoon_wB,                        FALSE,  TRUE,
5379     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5380   },
5381   {
5382     Xgrass,                             TRUE,   FALSE,
5383     EL_EMC_GRASS,                       -1, -1
5384   },
5385   {
5386     Ygrass_nB,                          FALSE,  FALSE,
5387     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
5388   },
5389   {
5390     Ygrass_eB,                          FALSE,  FALSE,
5391     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
5392   },
5393   {
5394     Ygrass_sB,                          FALSE,  FALSE,
5395     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
5396   },
5397   {
5398     Ygrass_wB,                          FALSE,  FALSE,
5399     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
5400   },
5401   {
5402     Xdirt,                              TRUE,   FALSE,
5403     EL_SAND,                            -1, -1
5404   },
5405   {
5406     Ydirt_nB,                           FALSE,  FALSE,
5407     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
5408   },
5409   {
5410     Ydirt_eB,                           FALSE,  FALSE,
5411     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
5412   },
5413   {
5414     Ydirt_sB,                           FALSE,  FALSE,
5415     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
5416   },
5417   {
5418     Ydirt_wB,                           FALSE,  FALSE,
5419     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
5420   },
5421   {
5422     Xacid_ne,                           TRUE,   FALSE,
5423     EL_ACID_POOL_TOPRIGHT,              -1, -1
5424   },
5425   {
5426     Xacid_se,                           TRUE,   FALSE,
5427     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
5428   },
5429   {
5430     Xacid_s,                            TRUE,   FALSE,
5431     EL_ACID_POOL_BOTTOM,                -1, -1
5432   },
5433   {
5434     Xacid_sw,                           TRUE,   FALSE,
5435     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
5436   },
5437   {
5438     Xacid_nw,                           TRUE,   FALSE,
5439     EL_ACID_POOL_TOPLEFT,               -1, -1
5440   },
5441   {
5442     Xacid_1,                            TRUE,   FALSE,
5443     EL_ACID,                            -1, -1
5444   },
5445   {
5446     Xacid_2,                            FALSE,  FALSE,
5447     EL_ACID,                            -1, -1
5448   },
5449   {
5450     Xacid_3,                            FALSE,  FALSE,
5451     EL_ACID,                            -1, -1
5452   },
5453   {
5454     Xacid_4,                            FALSE,  FALSE,
5455     EL_ACID,                            -1, -1
5456   },
5457   {
5458     Xacid_5,                            FALSE,  FALSE,
5459     EL_ACID,                            -1, -1
5460   },
5461   {
5462     Xacid_6,                            FALSE,  FALSE,
5463     EL_ACID,                            -1, -1
5464   },
5465   {
5466     Xacid_7,                            FALSE,  FALSE,
5467     EL_ACID,                            -1, -1
5468   },
5469   {
5470     Xacid_8,                            FALSE,  FALSE,
5471     EL_ACID,                            -1, -1
5472   },
5473   {
5474     Xball_1,                            TRUE,   FALSE,
5475     EL_EMC_MAGIC_BALL,                  -1, -1
5476   },
5477   {
5478     Xball_1B,                           FALSE,  FALSE,
5479     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5480   },
5481   {
5482     Xball_2,                            FALSE,  FALSE,
5483     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5484   },
5485   {
5486     Xball_2B,                           FALSE,  FALSE,
5487     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5488   },
5489   {
5490     Yball_eat,                          FALSE,  FALSE,
5491     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
5492   },
5493   {
5494     Ykey_1_eat,                         FALSE,  FALSE,
5495     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
5496   },
5497   {
5498     Ykey_2_eat,                         FALSE,  FALSE,
5499     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
5500   },
5501   {
5502     Ykey_3_eat,                         FALSE,  FALSE,
5503     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
5504   },
5505   {
5506     Ykey_4_eat,                         FALSE,  FALSE,
5507     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
5508   },
5509   {
5510     Ykey_5_eat,                         FALSE,  FALSE,
5511     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
5512   },
5513   {
5514     Ykey_6_eat,                         FALSE,  FALSE,
5515     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
5516   },
5517   {
5518     Ykey_7_eat,                         FALSE,  FALSE,
5519     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
5520   },
5521   {
5522     Ykey_8_eat,                         FALSE,  FALSE,
5523     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
5524   },
5525   {
5526     Ylenses_eat,                        FALSE,  FALSE,
5527     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
5528   },
5529   {
5530     Ymagnify_eat,                       FALSE,  FALSE,
5531     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
5532   },
5533   {
5534     Ygrass_eat,                         FALSE,  FALSE,
5535     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
5536   },
5537   {
5538     Ydirt_eat,                          FALSE,  FALSE,
5539     EL_SAND,                            ACTION_SNAPPING, -1
5540   },
5541   {
5542     Xgrow_ns,                           TRUE,   FALSE,
5543     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
5544   },
5545   {
5546     Ygrow_ns_eat,                       FALSE,  FALSE,
5547     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
5548   },
5549   {
5550     Xgrow_ew,                           TRUE,   FALSE,
5551     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
5552   },
5553   {
5554     Ygrow_ew_eat,                       FALSE,  FALSE,
5555     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
5556   },
5557   {
5558     Xwonderwall,                        TRUE,   FALSE,
5559     EL_MAGIC_WALL,                      -1, -1
5560   },
5561   {
5562     XwonderwallB,                       FALSE,  FALSE,
5563     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
5564   },
5565   {
5566     Xamoeba_1,                          TRUE,   FALSE,
5567     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5568   },
5569   {
5570     Xamoeba_2,                          FALSE,  FALSE,
5571     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5572   },
5573   {
5574     Xamoeba_3,                          FALSE,  FALSE,
5575     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5576   },
5577   {
5578     Xamoeba_4,                          FALSE,  FALSE,
5579     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5580   },
5581   {
5582     Xamoeba_5,                          TRUE,   FALSE,
5583     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5584   },
5585   {
5586     Xamoeba_6,                          FALSE,  FALSE,
5587     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5588   },
5589   {
5590     Xamoeba_7,                          FALSE,  FALSE,
5591     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5592   },
5593   {
5594     Xamoeba_8,                          FALSE,  FALSE,
5595     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5596   },
5597   {
5598     Xdoor_1,                            TRUE,   FALSE,
5599     EL_EM_GATE_1,                       -1, -1
5600   },
5601   {
5602     Xdoor_2,                            TRUE,   FALSE,
5603     EL_EM_GATE_2,                       -1, -1
5604   },
5605   {
5606     Xdoor_3,                            TRUE,   FALSE,
5607     EL_EM_GATE_3,                       -1, -1
5608   },
5609   {
5610     Xdoor_4,                            TRUE,   FALSE,
5611     EL_EM_GATE_4,                       -1, -1
5612   },
5613   {
5614     Xdoor_5,                            TRUE,   FALSE,
5615     EL_EMC_GATE_5,                      -1, -1
5616   },
5617   {
5618     Xdoor_6,                            TRUE,   FALSE,
5619     EL_EMC_GATE_6,                      -1, -1
5620   },
5621   {
5622     Xdoor_7,                            TRUE,   FALSE,
5623     EL_EMC_GATE_7,                      -1, -1
5624   },
5625   {
5626     Xdoor_8,                            TRUE,   FALSE,
5627     EL_EMC_GATE_8,                      -1, -1
5628   },
5629   {
5630     Xkey_1,                             TRUE,   FALSE,
5631     EL_EM_KEY_1,                        -1, -1
5632   },
5633   {
5634     Xkey_2,                             TRUE,   FALSE,
5635     EL_EM_KEY_2,                        -1, -1
5636   },
5637   {
5638     Xkey_3,                             TRUE,   FALSE,
5639     EL_EM_KEY_3,                        -1, -1
5640   },
5641   {
5642     Xkey_4,                             TRUE,   FALSE,
5643     EL_EM_KEY_4,                        -1, -1
5644   },
5645   {
5646     Xkey_5,                             TRUE,   FALSE,
5647     EL_EMC_KEY_5,                       -1, -1
5648   },
5649   {
5650     Xkey_6,                             TRUE,   FALSE,
5651     EL_EMC_KEY_6,                       -1, -1
5652   },
5653   {
5654     Xkey_7,                             TRUE,   FALSE,
5655     EL_EMC_KEY_7,                       -1, -1
5656   },
5657   {
5658     Xkey_8,                             TRUE,   FALSE,
5659     EL_EMC_KEY_8,                       -1, -1
5660   },
5661   {
5662     Xwind_n,                            TRUE,   FALSE,
5663     EL_BALLOON_SWITCH_UP,               -1, -1
5664   },
5665   {
5666     Xwind_e,                            TRUE,   FALSE,
5667     EL_BALLOON_SWITCH_RIGHT,            -1, -1
5668   },
5669   {
5670     Xwind_s,                            TRUE,   FALSE,
5671     EL_BALLOON_SWITCH_DOWN,             -1, -1
5672   },
5673   {
5674     Xwind_w,                            TRUE,   FALSE,
5675     EL_BALLOON_SWITCH_LEFT,             -1, -1
5676   },
5677   {
5678     Xwind_nesw,                         TRUE,   FALSE,
5679     EL_BALLOON_SWITCH_ANY,              -1, -1
5680   },
5681   {
5682     Xwind_stop,                         TRUE,   FALSE,
5683     EL_BALLOON_SWITCH_NONE,             -1, -1
5684   },
5685   {
5686     Xexit,                              TRUE,   FALSE,
5687     EL_EM_EXIT_CLOSED,                  -1, -1
5688   },
5689   {
5690     Xexit_1,                            TRUE,   FALSE,
5691     EL_EM_EXIT_OPEN,                    -1, -1
5692   },
5693   {
5694     Xexit_2,                            FALSE,  FALSE,
5695     EL_EM_EXIT_OPEN,                    -1, -1
5696   },
5697   {
5698     Xexit_3,                            FALSE,  FALSE,
5699     EL_EM_EXIT_OPEN,                    -1, -1
5700   },
5701   {
5702     Xdynamite,                          TRUE,   FALSE,
5703     EL_EM_DYNAMITE,                     -1, -1
5704   },
5705   {
5706     Ydynamite_eat,                      FALSE,  FALSE,
5707     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
5708   },
5709   {
5710     Xdynamite_1,                        TRUE,   FALSE,
5711     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5712   },
5713   {
5714     Xdynamite_2,                        FALSE,  FALSE,
5715     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5716   },
5717   {
5718     Xdynamite_3,                        FALSE,  FALSE,
5719     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5720   },
5721   {
5722     Xdynamite_4,                        FALSE,  FALSE,
5723     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5724   },
5725   {
5726     Xbumper,                            TRUE,   FALSE,
5727     EL_EMC_SPRING_BUMPER,               -1, -1
5728   },
5729   {
5730     XbumperB,                           FALSE,  FALSE,
5731     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
5732   },
5733   {
5734     Xwheel,                             TRUE,   FALSE,
5735     EL_ROBOT_WHEEL,                     -1, -1
5736   },
5737   {
5738     XwheelB,                            FALSE,  FALSE,
5739     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
5740   },
5741   {
5742     Xswitch,                            TRUE,   FALSE,
5743     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
5744   },
5745   {
5746     XswitchB,                           FALSE,  FALSE,
5747     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
5748   },
5749   {
5750     Xsand,                              TRUE,   FALSE,
5751     EL_QUICKSAND_EMPTY,                 -1, -1
5752   },
5753   {
5754     Xsand_stone,                        TRUE,   FALSE,
5755     EL_QUICKSAND_FULL,                  -1, -1
5756   },
5757   {
5758     Xsand_stonein_1,                    FALSE,  TRUE,
5759     EL_ROCK,                            ACTION_FILLING, -1
5760   },
5761   {
5762     Xsand_stonein_2,                    FALSE,  TRUE,
5763     EL_ROCK,                            ACTION_FILLING, -1
5764   },
5765   {
5766     Xsand_stonein_3,                    FALSE,  TRUE,
5767     EL_ROCK,                            ACTION_FILLING, -1
5768   },
5769   {
5770     Xsand_stonein_4,                    FALSE,  TRUE,
5771     EL_ROCK,                            ACTION_FILLING, -1
5772   },
5773   {
5774     Xsand_stonesand_1,                  FALSE,  FALSE,
5775     EL_QUICKSAND_EMPTYING,              -1, -1
5776   },
5777   {
5778     Xsand_stonesand_2,                  FALSE,  FALSE,
5779     EL_QUICKSAND_EMPTYING,              -1, -1
5780   },
5781   {
5782     Xsand_stonesand_3,                  FALSE,  FALSE,
5783     EL_QUICKSAND_EMPTYING,              -1, -1
5784   },
5785   {
5786     Xsand_stonesand_4,                  FALSE,  FALSE,
5787     EL_QUICKSAND_EMPTYING,              -1, -1
5788   },
5789   {
5790     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
5791     EL_QUICKSAND_EMPTYING,              -1, -1
5792   },
5793   {
5794     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
5795     EL_QUICKSAND_EMPTYING,              -1, -1
5796   },
5797   {
5798     Xsand_stoneout_1,                   FALSE,  FALSE,
5799     EL_ROCK,                            ACTION_EMPTYING, -1
5800   },
5801   {
5802     Xsand_stoneout_2,                   FALSE,  FALSE,
5803     EL_ROCK,                            ACTION_EMPTYING, -1
5804   },
5805   {
5806     Xsand_sandstone_1,                  FALSE,  FALSE,
5807     EL_QUICKSAND_FILLING,               -1, -1
5808   },
5809   {
5810     Xsand_sandstone_2,                  FALSE,  FALSE,
5811     EL_QUICKSAND_FILLING,               -1, -1
5812   },
5813   {
5814     Xsand_sandstone_3,                  FALSE,  FALSE,
5815     EL_QUICKSAND_FILLING,               -1, -1
5816   },
5817   {
5818     Xsand_sandstone_4,                  FALSE,  FALSE,
5819     EL_QUICKSAND_FILLING,               -1, -1
5820   },
5821   {
5822     Xplant,                             TRUE,   FALSE,
5823     EL_EMC_PLANT,                       -1, -1
5824   },
5825   {
5826     Yplant,                             FALSE,  FALSE,
5827     EL_EMC_PLANT,                       -1, -1
5828   },
5829   {
5830     Xlenses,                            TRUE,   FALSE,
5831     EL_EMC_LENSES,                      -1, -1
5832   },
5833   {
5834     Xmagnify,                           TRUE,   FALSE,
5835     EL_EMC_MAGNIFIER,                   -1, -1
5836   },
5837   {
5838     Xdripper,                           TRUE,   FALSE,
5839     EL_EMC_DRIPPER,                     -1, -1
5840   },
5841   {
5842     XdripperB,                          FALSE,  FALSE,
5843     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
5844   },
5845   {
5846     Xfake_blank,                        TRUE,   FALSE,
5847     EL_INVISIBLE_WALL,                  -1, -1
5848   },
5849   {
5850     Xfake_blankB,                       FALSE,  FALSE,
5851     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
5852   },
5853   {
5854     Xfake_grass,                        TRUE,   FALSE,
5855     EL_EMC_FAKE_GRASS,                  -1, -1
5856   },
5857   {
5858     Xfake_grassB,                       FALSE,  FALSE,
5859     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
5860   },
5861   {
5862     Xfake_door_1,                       TRUE,   FALSE,
5863     EL_EM_GATE_1_GRAY,                  -1, -1
5864   },
5865   {
5866     Xfake_door_2,                       TRUE,   FALSE,
5867     EL_EM_GATE_2_GRAY,                  -1, -1
5868   },
5869   {
5870     Xfake_door_3,                       TRUE,   FALSE,
5871     EL_EM_GATE_3_GRAY,                  -1, -1
5872   },
5873   {
5874     Xfake_door_4,                       TRUE,   FALSE,
5875     EL_EM_GATE_4_GRAY,                  -1, -1
5876   },
5877   {
5878     Xfake_door_5,                       TRUE,   FALSE,
5879     EL_EMC_GATE_5_GRAY,                 -1, -1
5880   },
5881   {
5882     Xfake_door_6,                       TRUE,   FALSE,
5883     EL_EMC_GATE_6_GRAY,                 -1, -1
5884   },
5885   {
5886     Xfake_door_7,                       TRUE,   FALSE,
5887     EL_EMC_GATE_7_GRAY,                 -1, -1
5888   },
5889   {
5890     Xfake_door_8,                       TRUE,   FALSE,
5891     EL_EMC_GATE_8_GRAY,                 -1, -1
5892   },
5893   {
5894     Xfake_acid_1,                       TRUE,   FALSE,
5895     EL_EMC_FAKE_ACID,                   -1, -1
5896   },
5897   {
5898     Xfake_acid_2,                       FALSE,  FALSE,
5899     EL_EMC_FAKE_ACID,                   -1, -1
5900   },
5901   {
5902     Xfake_acid_3,                       FALSE,  FALSE,
5903     EL_EMC_FAKE_ACID,                   -1, -1
5904   },
5905   {
5906     Xfake_acid_4,                       FALSE,  FALSE,
5907     EL_EMC_FAKE_ACID,                   -1, -1
5908   },
5909   {
5910     Xfake_acid_5,                       FALSE,  FALSE,
5911     EL_EMC_FAKE_ACID,                   -1, -1
5912   },
5913   {
5914     Xfake_acid_6,                       FALSE,  FALSE,
5915     EL_EMC_FAKE_ACID,                   -1, -1
5916   },
5917   {
5918     Xfake_acid_7,                       FALSE,  FALSE,
5919     EL_EMC_FAKE_ACID,                   -1, -1
5920   },
5921   {
5922     Xfake_acid_8,                       FALSE,  FALSE,
5923     EL_EMC_FAKE_ACID,                   -1, -1
5924   },
5925   {
5926     Xsteel_1,                           TRUE,   FALSE,
5927     EL_STEELWALL,                       -1, -1
5928   },
5929   {
5930     Xsteel_2,                           TRUE,   FALSE,
5931     EL_EMC_STEELWALL_2,                 -1, -1
5932   },
5933   {
5934     Xsteel_3,                           TRUE,   FALSE,
5935     EL_EMC_STEELWALL_3,                 -1, -1
5936   },
5937   {
5938     Xsteel_4,                           TRUE,   FALSE,
5939     EL_EMC_STEELWALL_4,                 -1, -1
5940   },
5941   {
5942     Xwall_1,                            TRUE,   FALSE,
5943     EL_WALL,                            -1, -1
5944   },
5945   {
5946     Xwall_2,                            TRUE,   FALSE,
5947     EL_EMC_WALL_14,                     -1, -1
5948   },
5949   {
5950     Xwall_3,                            TRUE,   FALSE,
5951     EL_EMC_WALL_15,                     -1, -1
5952   },
5953   {
5954     Xwall_4,                            TRUE,   FALSE,
5955     EL_EMC_WALL_16,                     -1, -1
5956   },
5957   {
5958     Xround_wall_1,                      TRUE,   FALSE,
5959     EL_WALL_SLIPPERY,                   -1, -1
5960   },
5961   {
5962     Xround_wall_2,                      TRUE,   FALSE,
5963     EL_EMC_WALL_SLIPPERY_2,             -1, -1
5964   },
5965   {
5966     Xround_wall_3,                      TRUE,   FALSE,
5967     EL_EMC_WALL_SLIPPERY_3,             -1, -1
5968   },
5969   {
5970     Xround_wall_4,                      TRUE,   FALSE,
5971     EL_EMC_WALL_SLIPPERY_4,             -1, -1
5972   },
5973   {
5974     Xdecor_1,                           TRUE,   FALSE,
5975     EL_EMC_WALL_8,                      -1, -1
5976   },
5977   {
5978     Xdecor_2,                           TRUE,   FALSE,
5979     EL_EMC_WALL_6,                      -1, -1
5980   },
5981   {
5982     Xdecor_3,                           TRUE,   FALSE,
5983     EL_EMC_WALL_4,                      -1, -1
5984   },
5985   {
5986     Xdecor_4,                           TRUE,   FALSE,
5987     EL_EMC_WALL_7,                      -1, -1
5988   },
5989   {
5990     Xdecor_5,                           TRUE,   FALSE,
5991     EL_EMC_WALL_5,                      -1, -1
5992   },
5993   {
5994     Xdecor_6,                           TRUE,   FALSE,
5995     EL_EMC_WALL_9,                      -1, -1
5996   },
5997   {
5998     Xdecor_7,                           TRUE,   FALSE,
5999     EL_EMC_WALL_10,                     -1, -1
6000   },
6001   {
6002     Xdecor_8,                           TRUE,   FALSE,
6003     EL_EMC_WALL_1,                      -1, -1
6004   },
6005   {
6006     Xdecor_9,                           TRUE,   FALSE,
6007     EL_EMC_WALL_2,                      -1, -1
6008   },
6009   {
6010     Xdecor_10,                          TRUE,   FALSE,
6011     EL_EMC_WALL_3,                      -1, -1
6012   },
6013   {
6014     Xdecor_11,                          TRUE,   FALSE,
6015     EL_EMC_WALL_11,                     -1, -1
6016   },
6017   {
6018     Xdecor_12,                          TRUE,   FALSE,
6019     EL_EMC_WALL_12,                     -1, -1
6020   },
6021   {
6022     Xalpha_0,                           TRUE,   FALSE,
6023     EL_CHAR('0'),                       -1, -1
6024   },
6025   {
6026     Xalpha_1,                           TRUE,   FALSE,
6027     EL_CHAR('1'),                       -1, -1
6028   },
6029   {
6030     Xalpha_2,                           TRUE,   FALSE,
6031     EL_CHAR('2'),                       -1, -1
6032   },
6033   {
6034     Xalpha_3,                           TRUE,   FALSE,
6035     EL_CHAR('3'),                       -1, -1
6036   },
6037   {
6038     Xalpha_4,                           TRUE,   FALSE,
6039     EL_CHAR('4'),                       -1, -1
6040   },
6041   {
6042     Xalpha_5,                           TRUE,   FALSE,
6043     EL_CHAR('5'),                       -1, -1
6044   },
6045   {
6046     Xalpha_6,                           TRUE,   FALSE,
6047     EL_CHAR('6'),                       -1, -1
6048   },
6049   {
6050     Xalpha_7,                           TRUE,   FALSE,
6051     EL_CHAR('7'),                       -1, -1
6052   },
6053   {
6054     Xalpha_8,                           TRUE,   FALSE,
6055     EL_CHAR('8'),                       -1, -1
6056   },
6057   {
6058     Xalpha_9,                           TRUE,   FALSE,
6059     EL_CHAR('9'),                       -1, -1
6060   },
6061   {
6062     Xalpha_excla,                       TRUE,   FALSE,
6063     EL_CHAR('!'),                       -1, -1
6064   },
6065   {
6066     Xalpha_quote,                       TRUE,   FALSE,
6067     EL_CHAR('"'),                       -1, -1
6068   },
6069   {
6070     Xalpha_comma,                       TRUE,   FALSE,
6071     EL_CHAR(','),                       -1, -1
6072   },
6073   {
6074     Xalpha_minus,                       TRUE,   FALSE,
6075     EL_CHAR('-'),                       -1, -1
6076   },
6077   {
6078     Xalpha_perio,                       TRUE,   FALSE,
6079     EL_CHAR('.'),                       -1, -1
6080   },
6081   {
6082     Xalpha_colon,                       TRUE,   FALSE,
6083     EL_CHAR(':'),                       -1, -1
6084   },
6085   {
6086     Xalpha_quest,                       TRUE,   FALSE,
6087     EL_CHAR('?'),                       -1, -1
6088   },
6089   {
6090     Xalpha_a,                           TRUE,   FALSE,
6091     EL_CHAR('A'),                       -1, -1
6092   },
6093   {
6094     Xalpha_b,                           TRUE,   FALSE,
6095     EL_CHAR('B'),                       -1, -1
6096   },
6097   {
6098     Xalpha_c,                           TRUE,   FALSE,
6099     EL_CHAR('C'),                       -1, -1
6100   },
6101   {
6102     Xalpha_d,                           TRUE,   FALSE,
6103     EL_CHAR('D'),                       -1, -1
6104   },
6105   {
6106     Xalpha_e,                           TRUE,   FALSE,
6107     EL_CHAR('E'),                       -1, -1
6108   },
6109   {
6110     Xalpha_f,                           TRUE,   FALSE,
6111     EL_CHAR('F'),                       -1, -1
6112   },
6113   {
6114     Xalpha_g,                           TRUE,   FALSE,
6115     EL_CHAR('G'),                       -1, -1
6116   },
6117   {
6118     Xalpha_h,                           TRUE,   FALSE,
6119     EL_CHAR('H'),                       -1, -1
6120   },
6121   {
6122     Xalpha_i,                           TRUE,   FALSE,
6123     EL_CHAR('I'),                       -1, -1
6124   },
6125   {
6126     Xalpha_j,                           TRUE,   FALSE,
6127     EL_CHAR('J'),                       -1, -1
6128   },
6129   {
6130     Xalpha_k,                           TRUE,   FALSE,
6131     EL_CHAR('K'),                       -1, -1
6132   },
6133   {
6134     Xalpha_l,                           TRUE,   FALSE,
6135     EL_CHAR('L'),                       -1, -1
6136   },
6137   {
6138     Xalpha_m,                           TRUE,   FALSE,
6139     EL_CHAR('M'),                       -1, -1
6140   },
6141   {
6142     Xalpha_n,                           TRUE,   FALSE,
6143     EL_CHAR('N'),                       -1, -1
6144   },
6145   {
6146     Xalpha_o,                           TRUE,   FALSE,
6147     EL_CHAR('O'),                       -1, -1
6148   },
6149   {
6150     Xalpha_p,                           TRUE,   FALSE,
6151     EL_CHAR('P'),                       -1, -1
6152   },
6153   {
6154     Xalpha_q,                           TRUE,   FALSE,
6155     EL_CHAR('Q'),                       -1, -1
6156   },
6157   {
6158     Xalpha_r,                           TRUE,   FALSE,
6159     EL_CHAR('R'),                       -1, -1
6160   },
6161   {
6162     Xalpha_s,                           TRUE,   FALSE,
6163     EL_CHAR('S'),                       -1, -1
6164   },
6165   {
6166     Xalpha_t,                           TRUE,   FALSE,
6167     EL_CHAR('T'),                       -1, -1
6168   },
6169   {
6170     Xalpha_u,                           TRUE,   FALSE,
6171     EL_CHAR('U'),                       -1, -1
6172   },
6173   {
6174     Xalpha_v,                           TRUE,   FALSE,
6175     EL_CHAR('V'),                       -1, -1
6176   },
6177   {
6178     Xalpha_w,                           TRUE,   FALSE,
6179     EL_CHAR('W'),                       -1, -1
6180   },
6181   {
6182     Xalpha_x,                           TRUE,   FALSE,
6183     EL_CHAR('X'),                       -1, -1
6184   },
6185   {
6186     Xalpha_y,                           TRUE,   FALSE,
6187     EL_CHAR('Y'),                       -1, -1
6188   },
6189   {
6190     Xalpha_z,                           TRUE,   FALSE,
6191     EL_CHAR('Z'),                       -1, -1
6192   },
6193   {
6194     Xalpha_arrow_e,                     TRUE,   FALSE,
6195     EL_CHAR('>'),                       -1, -1
6196   },
6197   {
6198     Xalpha_arrow_w,                     TRUE,   FALSE,
6199     EL_CHAR('<'),                       -1, -1
6200   },
6201   {
6202     Xalpha_copyr,                       TRUE,   FALSE,
6203     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
6204   },
6205
6206   {
6207     Xboom_bug,                          FALSE,  FALSE,
6208     EL_BUG,                             ACTION_EXPLODING, -1
6209   },
6210   {
6211     Xboom_bomb,                         FALSE,  FALSE,
6212     EL_BOMB,                            ACTION_EXPLODING, -1
6213   },
6214   {
6215     Xboom_android,                      FALSE,  FALSE,
6216     EL_EMC_ANDROID,                     ACTION_OTHER, -1
6217   },
6218   {
6219     Xboom_1,                            FALSE,  FALSE,
6220     EL_DEFAULT,                         ACTION_EXPLODING, -1
6221   },
6222   {
6223     Xboom_2,                            FALSE,  FALSE,
6224     EL_DEFAULT,                         ACTION_EXPLODING, -1
6225   },
6226   {
6227     Znormal,                            FALSE,  FALSE,
6228     EL_EMPTY,                           -1, -1
6229   },
6230   {
6231     Zdynamite,                          FALSE,  FALSE,
6232     EL_EMPTY,                           -1, -1
6233   },
6234   {
6235     Zplayer,                            FALSE,  FALSE,
6236     EL_EMPTY,                           -1, -1
6237   },
6238   {
6239     ZBORDER,                            FALSE,  FALSE,
6240     EL_EMPTY,                           -1, -1
6241   },
6242
6243   {
6244     -1,                                 FALSE,  FALSE,
6245     -1,                                 -1, -1
6246   }
6247 };
6248
6249 static struct Mapping_EM_to_RND_player
6250 {
6251   int action_em;
6252   int player_nr;
6253
6254   int element_rnd;
6255   int action;
6256   int direction;
6257 }
6258 em_player_mapping_list[] =
6259 {
6260   {
6261     SPR_walk + 0,                       0,
6262     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
6263   },
6264   {
6265     SPR_walk + 1,                       0,
6266     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
6267   },
6268   {
6269     SPR_walk + 2,                       0,
6270     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
6271   },
6272   {
6273     SPR_walk + 3,                       0,
6274     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
6275   },
6276   {
6277     SPR_push + 0,                       0,
6278     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
6279   },
6280   {
6281     SPR_push + 1,                       0,
6282     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
6283   },
6284   {
6285     SPR_push + 2,                       0,
6286     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
6287   },
6288   {
6289     SPR_push + 3,                       0,
6290     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
6291   },
6292   {
6293     SPR_spray + 0,                      0,
6294     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
6295   },
6296   {
6297     SPR_spray + 1,                      0,
6298     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6299   },
6300   {
6301     SPR_spray + 2,                      0,
6302     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
6303   },
6304   {
6305     SPR_spray + 3,                      0,
6306     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
6307   },
6308   {
6309     SPR_walk + 0,                       1,
6310     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
6311   },
6312   {
6313     SPR_walk + 1,                       1,
6314     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
6315   },
6316   {
6317     SPR_walk + 2,                       1,
6318     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
6319   },
6320   {
6321     SPR_walk + 3,                       1,
6322     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
6323   },
6324   {
6325     SPR_push + 0,                       1,
6326     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
6327   },
6328   {
6329     SPR_push + 1,                       1,
6330     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
6331   },
6332   {
6333     SPR_push + 2,                       1,
6334     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
6335   },
6336   {
6337     SPR_push + 3,                       1,
6338     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
6339   },
6340   {
6341     SPR_spray + 0,                      1,
6342     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
6343   },
6344   {
6345     SPR_spray + 1,                      1,
6346     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6347   },
6348   {
6349     SPR_spray + 2,                      1,
6350     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
6351   },
6352   {
6353     SPR_spray + 3,                      1,
6354     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
6355   },
6356   {
6357     SPR_still,                          0,
6358     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
6359   },
6360   {
6361     SPR_still,                          1,
6362     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
6363   },
6364   {
6365     SPR_walk + 0,                       2,
6366     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
6367   },
6368   {
6369     SPR_walk + 1,                       2,
6370     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
6371   },
6372   {
6373     SPR_walk + 2,                       2,
6374     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
6375   },
6376   {
6377     SPR_walk + 3,                       2,
6378     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
6379   },
6380   {
6381     SPR_push + 0,                       2,
6382     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
6383   },
6384   {
6385     SPR_push + 1,                       2,
6386     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
6387   },
6388   {
6389     SPR_push + 2,                       2,
6390     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
6391   },
6392   {
6393     SPR_push + 3,                       2,
6394     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
6395   },
6396   {
6397     SPR_spray + 0,                      2,
6398     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
6399   },
6400   {
6401     SPR_spray + 1,                      2,
6402     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6403   },
6404   {
6405     SPR_spray + 2,                      2,
6406     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
6407   },
6408   {
6409     SPR_spray + 3,                      2,
6410     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
6411   },
6412   {
6413     SPR_walk + 0,                       3,
6414     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
6415   },
6416   {
6417     SPR_walk + 1,                       3,
6418     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
6419   },
6420   {
6421     SPR_walk + 2,                       3,
6422     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
6423   },
6424   {
6425     SPR_walk + 3,                       3,
6426     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
6427   },
6428   {
6429     SPR_push + 0,                       3,
6430     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
6431   },
6432   {
6433     SPR_push + 1,                       3,
6434     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
6435   },
6436   {
6437     SPR_push + 2,                       3,
6438     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
6439   },
6440   {
6441     SPR_push + 3,                       3,
6442     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
6443   },
6444   {
6445     SPR_spray + 0,                      3,
6446     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
6447   },
6448   {
6449     SPR_spray + 1,                      3,
6450     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6451   },
6452   {
6453     SPR_spray + 2,                      3,
6454     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
6455   },
6456   {
6457     SPR_spray + 3,                      3,
6458     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
6459   },
6460   {
6461     SPR_still,                          2,
6462     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
6463   },
6464   {
6465     SPR_still,                          3,
6466     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
6467   },
6468
6469   {
6470     -1,                                 -1,
6471     -1,                                 -1, -1
6472   }
6473 };
6474
6475 int map_element_RND_to_EM(int element_rnd)
6476 {
6477   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6478   static boolean mapping_initialized = FALSE;
6479
6480   if (!mapping_initialized)
6481   {
6482     int i;
6483
6484     /* return "Xalpha_quest" for all undefined elements in mapping array */
6485     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6486       mapping_RND_to_EM[i] = Xalpha_quest;
6487
6488     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6489       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6490         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6491           em_object_mapping_list[i].element_em;
6492
6493     mapping_initialized = TRUE;
6494   }
6495
6496   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6497     return mapping_RND_to_EM[element_rnd];
6498
6499   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6500
6501   return EL_UNKNOWN;
6502 }
6503
6504 int map_element_EM_to_RND(int element_em)
6505 {
6506   static unsigned short mapping_EM_to_RND[TILE_MAX];
6507   static boolean mapping_initialized = FALSE;
6508
6509   if (!mapping_initialized)
6510   {
6511     int i;
6512
6513     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6514     for (i = 0; i < TILE_MAX; i++)
6515       mapping_EM_to_RND[i] = EL_UNKNOWN;
6516
6517     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6518       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6519         em_object_mapping_list[i].element_rnd;
6520
6521     mapping_initialized = TRUE;
6522   }
6523
6524   if (element_em >= 0 && element_em < TILE_MAX)
6525     return mapping_EM_to_RND[element_em];
6526
6527   Error(ERR_WARN, "invalid EM level element %d", element_em);
6528
6529   return EL_UNKNOWN;
6530 }
6531
6532 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6533 {
6534   struct LevelInfo_EM *level_em = level->native_em_level;
6535   struct LEVEL *lev = level_em->lev;
6536   int i, j;
6537
6538   for (i = 0; i < TILE_MAX; i++)
6539     lev->android_array[i] = Xblank;
6540
6541   for (i = 0; i < level->num_android_clone_elements; i++)
6542   {
6543     int element_rnd = level->android_clone_element[i];
6544     int element_em = map_element_RND_to_EM(element_rnd);
6545
6546     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6547       if (em_object_mapping_list[j].element_rnd == element_rnd)
6548         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6549   }
6550 }
6551
6552 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6553 {
6554   struct LevelInfo_EM *level_em = level->native_em_level;
6555   struct LEVEL *lev = level_em->lev;
6556   int i, j;
6557
6558   level->num_android_clone_elements = 0;
6559
6560   for (i = 0; i < TILE_MAX; i++)
6561   {
6562     int element_em = lev->android_array[i];
6563     int element_rnd;
6564     boolean element_found = FALSE;
6565
6566     if (element_em == Xblank)
6567       continue;
6568
6569     element_rnd = map_element_EM_to_RND(element_em);
6570
6571     for (j = 0; j < level->num_android_clone_elements; j++)
6572       if (level->android_clone_element[j] == element_rnd)
6573         element_found = TRUE;
6574
6575     if (!element_found)
6576     {
6577       level->android_clone_element[level->num_android_clone_elements++] =
6578         element_rnd;
6579
6580       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6581         break;
6582     }
6583   }
6584
6585   if (level->num_android_clone_elements == 0)
6586   {
6587     level->num_android_clone_elements = 1;
6588     level->android_clone_element[0] = EL_EMPTY;
6589   }
6590 }
6591
6592 int map_direction_RND_to_EM(int direction)
6593 {
6594   return (direction == MV_UP    ? 0 :
6595           direction == MV_RIGHT ? 1 :
6596           direction == MV_DOWN  ? 2 :
6597           direction == MV_LEFT  ? 3 :
6598           -1);
6599 }
6600
6601 int map_direction_EM_to_RND(int direction)
6602 {
6603   return (direction == 0 ? MV_UP    :
6604           direction == 1 ? MV_RIGHT :
6605           direction == 2 ? MV_DOWN  :
6606           direction == 3 ? MV_LEFT  :
6607           MV_NONE);
6608 }
6609
6610 int map_element_RND_to_SP(int element_rnd)
6611 {
6612   int element_sp = 0x20;        /* map unknown elements to yellow "hardware" */
6613
6614   if (element_rnd >= EL_SP_START &&
6615       element_rnd <= EL_SP_END)
6616     element_sp = element_rnd - EL_SP_START;
6617   else if (element_rnd == EL_EMPTY_SPACE)
6618     element_sp = 0x00;
6619   else if (element_rnd == EL_INVISIBLE_WALL)
6620     element_sp = 0x28;
6621
6622   return element_sp;
6623 }
6624
6625 int map_element_SP_to_RND(int element_sp)
6626 {
6627   int element_rnd = EL_UNKNOWN;
6628
6629   if (element_sp >= 0x00 &&
6630       element_sp <= 0x27)
6631     element_rnd = EL_SP_START + element_sp;
6632   else if (element_sp == 0x28)
6633     element_rnd = EL_INVISIBLE_WALL;
6634
6635   return element_rnd;
6636 }
6637
6638 int map_action_SP_to_RND(int action_sp)
6639 {
6640   switch (action_sp)
6641   {
6642     case actActive:             return ACTION_ACTIVE;
6643     case actImpact:             return ACTION_IMPACT;
6644     case actExploding:          return ACTION_EXPLODING;
6645     case actDigging:            return ACTION_DIGGING;
6646     case actSnapping:           return ACTION_SNAPPING;
6647     case actCollecting:         return ACTION_COLLECTING;
6648     case actPassing:            return ACTION_PASSING;
6649     case actPushing:            return ACTION_PUSHING;
6650     case actDropping:           return ACTION_DROPPING;
6651
6652     default:                    return ACTION_DEFAULT;
6653   }
6654 }
6655
6656 int get_next_element(int element)
6657 {
6658   switch (element)
6659   {
6660     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
6661     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
6662     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
6663     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
6664     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
6665     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
6666     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
6667     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
6668     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
6669     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
6670     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
6671
6672     default:                            return element;
6673   }
6674 }
6675
6676 int el_act_dir2img(int element, int action, int direction)
6677 {
6678   element = GFX_ELEMENT(element);
6679   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6680
6681   /* direction_graphic[][] == graphic[] for undefined direction graphics */
6682   return element_info[element].direction_graphic[action][direction];
6683 }
6684
6685 static int el_act_dir2crm(int element, int action, int direction)
6686 {
6687   element = GFX_ELEMENT(element);
6688   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6689
6690   /* direction_graphic[][] == graphic[] for undefined direction graphics */
6691   return element_info[element].direction_crumbled[action][direction];
6692 }
6693
6694 int el_act2img(int element, int action)
6695 {
6696   element = GFX_ELEMENT(element);
6697
6698   return element_info[element].graphic[action];
6699 }
6700
6701 int el_act2crm(int element, int action)
6702 {
6703   element = GFX_ELEMENT(element);
6704
6705   return element_info[element].crumbled[action];
6706 }
6707
6708 int el_dir2img(int element, int direction)
6709 {
6710   element = GFX_ELEMENT(element);
6711
6712   return el_act_dir2img(element, ACTION_DEFAULT, direction);
6713 }
6714
6715 int el2baseimg(int element)
6716 {
6717   return element_info[element].graphic[ACTION_DEFAULT];
6718 }
6719
6720 int el2img(int element)
6721 {
6722   element = GFX_ELEMENT(element);
6723
6724   return element_info[element].graphic[ACTION_DEFAULT];
6725 }
6726
6727 int el2edimg(int element)
6728 {
6729   element = GFX_ELEMENT(element);
6730
6731   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6732 }
6733
6734 int el2preimg(int element)
6735 {
6736   element = GFX_ELEMENT(element);
6737
6738   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6739 }
6740
6741 int el2panelimg(int element)
6742 {
6743   element = GFX_ELEMENT(element);
6744
6745   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6746 }
6747
6748 int font2baseimg(int font_nr)
6749 {
6750   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6751 }
6752
6753 int getBeltNrFromBeltElement(int element)
6754 {
6755   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6756           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6757           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6758 }
6759
6760 int getBeltNrFromBeltActiveElement(int element)
6761 {
6762   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6763           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6764           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6765 }
6766
6767 int getBeltNrFromBeltSwitchElement(int element)
6768 {
6769   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6770           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6771           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6772 }
6773
6774 int getBeltDirNrFromBeltElement(int element)
6775 {
6776   static int belt_base_element[4] =
6777   {
6778     EL_CONVEYOR_BELT_1_LEFT,
6779     EL_CONVEYOR_BELT_2_LEFT,
6780     EL_CONVEYOR_BELT_3_LEFT,
6781     EL_CONVEYOR_BELT_4_LEFT
6782   };
6783
6784   int belt_nr = getBeltNrFromBeltElement(element);
6785   int belt_dir_nr = element - belt_base_element[belt_nr];
6786
6787   return (belt_dir_nr % 3);
6788 }
6789
6790 int getBeltDirNrFromBeltSwitchElement(int element)
6791 {
6792   static int belt_base_element[4] =
6793   {
6794     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6795     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6796     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6797     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6798   };
6799
6800   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6801   int belt_dir_nr = element - belt_base_element[belt_nr];
6802
6803   return (belt_dir_nr % 3);
6804 }
6805
6806 int getBeltDirFromBeltElement(int element)
6807 {
6808   static int belt_move_dir[3] =
6809   {
6810     MV_LEFT,
6811     MV_NONE,
6812     MV_RIGHT
6813   };
6814
6815   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6816
6817   return belt_move_dir[belt_dir_nr];
6818 }
6819
6820 int getBeltDirFromBeltSwitchElement(int element)
6821 {
6822   static int belt_move_dir[3] =
6823   {
6824     MV_LEFT,
6825     MV_NONE,
6826     MV_RIGHT
6827   };
6828
6829   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6830
6831   return belt_move_dir[belt_dir_nr];
6832 }
6833
6834 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6835 {
6836   static int belt_base_element[4] =
6837   {
6838     EL_CONVEYOR_BELT_1_LEFT,
6839     EL_CONVEYOR_BELT_2_LEFT,
6840     EL_CONVEYOR_BELT_3_LEFT,
6841     EL_CONVEYOR_BELT_4_LEFT
6842   };
6843
6844   return belt_base_element[belt_nr] + belt_dir_nr;
6845 }
6846
6847 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6848 {
6849   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6850
6851   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6852 }
6853
6854 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6855 {
6856   static int belt_base_element[4] =
6857   {
6858     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6859     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6860     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6861     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6862   };
6863
6864   return belt_base_element[belt_nr] + belt_dir_nr;
6865 }
6866
6867 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6868 {
6869   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6870
6871   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6872 }
6873
6874 boolean getTeamMode_EM()
6875 {
6876   return game.team_mode;
6877 }
6878
6879 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6880 {
6881   int game_frame_delay_value;
6882
6883   game_frame_delay_value =
6884     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6885      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6886      GameFrameDelay);
6887
6888   if (tape.playing && tape.warp_forward && !tape.pausing)
6889     game_frame_delay_value = 0;
6890
6891   return game_frame_delay_value;
6892 }
6893
6894 unsigned int InitRND(int seed)
6895 {
6896   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6897     return InitEngineRandom_EM(seed);
6898   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6899     return InitEngineRandom_SP(seed);
6900   else
6901     return InitEngineRandom_RND(seed);
6902 }
6903
6904 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6905 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6906
6907 inline static int get_effective_element_EM(int tile, int frame_em)
6908 {
6909   int element             = object_mapping[tile].element_rnd;
6910   int action              = object_mapping[tile].action;
6911   boolean is_backside     = object_mapping[tile].is_backside;
6912   boolean action_removing = (action == ACTION_DIGGING ||
6913                              action == ACTION_SNAPPING ||
6914                              action == ACTION_COLLECTING);
6915
6916   if (frame_em < 7)
6917   {
6918     switch (tile)
6919     {
6920       case Yacid_splash_eB:
6921       case Yacid_splash_wB:
6922         return (frame_em > 5 ? EL_EMPTY : element);
6923
6924       default:
6925         return element;
6926     }
6927   }
6928   else  /* frame_em == 7 */
6929   {
6930     switch (tile)
6931     {
6932       case Yacid_splash_eB:
6933       case Yacid_splash_wB:
6934         return EL_EMPTY;
6935
6936       case Yemerald_stone:
6937         return EL_EMERALD;
6938
6939       case Ydiamond_stone:
6940         return EL_ROCK;
6941
6942       case Xdrip_stretch:
6943       case Xdrip_stretchB:
6944       case Ydrip_s1:
6945       case Ydrip_s1B:
6946       case Xball_1B:
6947       case Xball_2:
6948       case Xball_2B:
6949       case Yball_eat:
6950       case Ykey_1_eat:
6951       case Ykey_2_eat:
6952       case Ykey_3_eat:
6953       case Ykey_4_eat:
6954       case Ykey_5_eat:
6955       case Ykey_6_eat:
6956       case Ykey_7_eat:
6957       case Ykey_8_eat:
6958       case Ylenses_eat:
6959       case Ymagnify_eat:
6960       case Ygrass_eat:
6961       case Ydirt_eat:
6962       case Xsand_stonein_1:
6963       case Xsand_stonein_2:
6964       case Xsand_stonein_3:
6965       case Xsand_stonein_4:
6966         return element;
6967
6968       default:
6969         return (is_backside || action_removing ? EL_EMPTY : element);
6970     }
6971   }
6972 }
6973
6974 inline static boolean check_linear_animation_EM(int tile)
6975 {
6976   switch (tile)
6977   {
6978     case Xsand_stonesand_1:
6979     case Xsand_stonesand_quickout_1:
6980     case Xsand_sandstone_1:
6981     case Xsand_stonein_1:
6982     case Xsand_stoneout_1:
6983     case Xboom_1:
6984     case Xdynamite_1:
6985     case Ybug_w_n:
6986     case Ybug_n_e:
6987     case Ybug_e_s:
6988     case Ybug_s_w:
6989     case Ybug_e_n:
6990     case Ybug_s_e:
6991     case Ybug_w_s:
6992     case Ybug_n_w:
6993     case Ytank_w_n:
6994     case Ytank_n_e:
6995     case Ytank_e_s:
6996     case Ytank_s_w:
6997     case Ytank_e_n:
6998     case Ytank_s_e:
6999     case Ytank_w_s:
7000     case Ytank_n_w:
7001     case Yacid_splash_eB:
7002     case Yacid_splash_wB:
7003     case Yemerald_stone:
7004       return TRUE;
7005   }
7006
7007   return FALSE;
7008 }
7009
7010 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7011                                             boolean has_crumbled_graphics,
7012                                             int crumbled, int sync_frame)
7013 {
7014   /* if element can be crumbled, but certain action graphics are just empty
7015      space (like instantly snapping sand to empty space in 1 frame), do not
7016      treat these empty space graphics as crumbled graphics in EMC engine */
7017   if (crumbled == IMG_EMPTY_SPACE)
7018     has_crumbled_graphics = FALSE;
7019
7020   if (has_crumbled_graphics)
7021   {
7022     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7023     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7024                                            g_crumbled->anim_delay,
7025                                            g_crumbled->anim_mode,
7026                                            g_crumbled->anim_start_frame,
7027                                            sync_frame);
7028
7029     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7030                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7031
7032     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7033
7034     g_em->has_crumbled_graphics = TRUE;
7035   }
7036   else
7037   {
7038     g_em->crumbled_bitmap = NULL;
7039     g_em->crumbled_src_x = 0;
7040     g_em->crumbled_src_y = 0;
7041     g_em->crumbled_border_size = 0;
7042
7043     g_em->has_crumbled_graphics = FALSE;
7044   }
7045 }
7046
7047 void ResetGfxAnimation_EM(int x, int y, int tile)
7048 {
7049   GfxFrame[x][y] = 0;
7050 }
7051
7052 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7053                         int tile, int frame_em, int x, int y)
7054 {
7055   int action = object_mapping[tile].action;
7056   int direction = object_mapping[tile].direction;
7057   int effective_element = get_effective_element_EM(tile, frame_em);
7058   int graphic = (direction == MV_NONE ?
7059                  el_act2img(effective_element, action) :
7060                  el_act_dir2img(effective_element, action, direction));
7061   struct GraphicInfo *g = &graphic_info[graphic];
7062   int sync_frame;
7063   boolean action_removing = (action == ACTION_DIGGING ||
7064                              action == ACTION_SNAPPING ||
7065                              action == ACTION_COLLECTING);
7066   boolean action_moving   = (action == ACTION_FALLING ||
7067                              action == ACTION_MOVING ||
7068                              action == ACTION_PUSHING ||
7069                              action == ACTION_EATING ||
7070                              action == ACTION_FILLING ||
7071                              action == ACTION_EMPTYING);
7072   boolean action_falling  = (action == ACTION_FALLING ||
7073                              action == ACTION_FILLING ||
7074                              action == ACTION_EMPTYING);
7075
7076   /* special case: graphic uses "2nd movement tile" and has defined
7077      7 frames for movement animation (or less) => use default graphic
7078      for last (8th) frame which ends the movement animation */
7079   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7080   {
7081     action = ACTION_DEFAULT;    /* (keep action_* unchanged for now) */
7082     graphic = (direction == MV_NONE ?
7083                el_act2img(effective_element, action) :
7084                el_act_dir2img(effective_element, action, direction));
7085
7086     g = &graphic_info[graphic];
7087   }
7088
7089   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7090   {
7091     GfxFrame[x][y] = 0;
7092   }
7093   else if (action_moving)
7094   {
7095     boolean is_backside = object_mapping[tile].is_backside;
7096
7097     if (is_backside)
7098     {
7099       int direction = object_mapping[tile].direction;
7100       int move_dir = (action_falling ? MV_DOWN : direction);
7101
7102       GfxFrame[x][y]++;
7103
7104 #if 1
7105       /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7106       if (g->double_movement && frame_em == 0)
7107         GfxFrame[x][y] = 0;
7108 #endif
7109
7110       if (move_dir == MV_LEFT)
7111         GfxFrame[x - 1][y] = GfxFrame[x][y];
7112       else if (move_dir == MV_RIGHT)
7113         GfxFrame[x + 1][y] = GfxFrame[x][y];
7114       else if (move_dir == MV_UP)
7115         GfxFrame[x][y - 1] = GfxFrame[x][y];
7116       else if (move_dir == MV_DOWN)
7117         GfxFrame[x][y + 1] = GfxFrame[x][y];
7118     }
7119   }
7120   else
7121   {
7122     GfxFrame[x][y]++;
7123
7124     /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7125     if (tile == Xsand_stonesand_quickout_1 ||
7126         tile == Xsand_stonesand_quickout_2)
7127       GfxFrame[x][y]++;
7128   }
7129
7130   if (graphic_info[graphic].anim_global_sync)
7131     sync_frame = FrameCounter;
7132   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7133     sync_frame = GfxFrame[x][y];
7134   else
7135     sync_frame = 0;     /* playfield border (pseudo steel) */
7136
7137   SetRandomAnimationValue(x, y);
7138
7139   int frame = getAnimationFrame(g->anim_frames,
7140                                 g->anim_delay,
7141                                 g->anim_mode,
7142                                 g->anim_start_frame,
7143                                 sync_frame);
7144
7145   g_em->unique_identifier =
7146     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7147 }
7148
7149 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7150                                   int tile, int frame_em, int x, int y)
7151 {
7152   int action = object_mapping[tile].action;
7153   int direction = object_mapping[tile].direction;
7154   boolean is_backside = object_mapping[tile].is_backside;
7155   int effective_element = get_effective_element_EM(tile, frame_em);
7156   int effective_action = action;
7157   int graphic = (direction == MV_NONE ?
7158                  el_act2img(effective_element, effective_action) :
7159                  el_act_dir2img(effective_element, effective_action,
7160                                 direction));
7161   int crumbled = (direction == MV_NONE ?
7162                   el_act2crm(effective_element, effective_action) :
7163                   el_act_dir2crm(effective_element, effective_action,
7164                                  direction));
7165   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7166   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7167   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7168   struct GraphicInfo *g = &graphic_info[graphic];
7169   int sync_frame;
7170
7171   /* special case: graphic uses "2nd movement tile" and has defined
7172      7 frames for movement animation (or less) => use default graphic
7173      for last (8th) frame which ends the movement animation */
7174   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7175   {
7176     effective_action = ACTION_DEFAULT;
7177     graphic = (direction == MV_NONE ?
7178                el_act2img(effective_element, effective_action) :
7179                el_act_dir2img(effective_element, effective_action,
7180                               direction));
7181     crumbled = (direction == MV_NONE ?
7182                 el_act2crm(effective_element, effective_action) :
7183                 el_act_dir2crm(effective_element, effective_action,
7184                                direction));
7185
7186     g = &graphic_info[graphic];
7187   }
7188
7189   if (graphic_info[graphic].anim_global_sync)
7190     sync_frame = FrameCounter;
7191   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7192     sync_frame = GfxFrame[x][y];
7193   else
7194     sync_frame = 0;     /* playfield border (pseudo steel) */
7195
7196   SetRandomAnimationValue(x, y);
7197
7198   int frame = getAnimationFrame(g->anim_frames,
7199                                 g->anim_delay,
7200                                 g->anim_mode,
7201                                 g->anim_start_frame,
7202                                 sync_frame);
7203
7204   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7205                       g->double_movement && is_backside);
7206
7207   /* (updating the "crumbled" graphic definitions is probably not really needed,
7208      as animations for crumbled graphics can't be longer than one EMC cycle) */
7209   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7210                            sync_frame);
7211 }
7212
7213 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7214                                   int player_nr, int anim, int frame_em)
7215 {
7216   int element   = player_mapping[player_nr][anim].element_rnd;
7217   int action    = player_mapping[player_nr][anim].action;
7218   int direction = player_mapping[player_nr][anim].direction;
7219   int graphic = (direction == MV_NONE ?
7220                  el_act2img(element, action) :
7221                  el_act_dir2img(element, action, direction));
7222   struct GraphicInfo *g = &graphic_info[graphic];
7223   int sync_frame;
7224
7225   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7226
7227   stored_player[player_nr].StepFrame = frame_em;
7228
7229   sync_frame = stored_player[player_nr].Frame;
7230
7231   int frame = getAnimationFrame(g->anim_frames,
7232                                 g->anim_delay,
7233                                 g->anim_mode,
7234                                 g->anim_start_frame,
7235                                 sync_frame);
7236
7237   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7238                       &g_em->src_x, &g_em->src_y, FALSE);
7239 }
7240
7241 void InitGraphicInfo_EM(void)
7242 {
7243   int i, j, p;
7244
7245 #if DEBUG_EM_GFX
7246   int num_em_gfx_errors = 0;
7247
7248   if (graphic_info_em_object[0][0].bitmap == NULL)
7249   {
7250     /* EM graphics not yet initialized in em_open_all() */
7251
7252     return;
7253   }
7254
7255   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7256 #endif
7257
7258   /* always start with reliable default values */
7259   for (i = 0; i < TILE_MAX; i++)
7260   {
7261     object_mapping[i].element_rnd = EL_UNKNOWN;
7262     object_mapping[i].is_backside = FALSE;
7263     object_mapping[i].action = ACTION_DEFAULT;
7264     object_mapping[i].direction = MV_NONE;
7265   }
7266
7267   /* always start with reliable default values */
7268   for (p = 0; p < MAX_PLAYERS; p++)
7269   {
7270     for (i = 0; i < SPR_MAX; i++)
7271     {
7272       player_mapping[p][i].element_rnd = EL_UNKNOWN;
7273       player_mapping[p][i].action = ACTION_DEFAULT;
7274       player_mapping[p][i].direction = MV_NONE;
7275     }
7276   }
7277
7278   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7279   {
7280     int e = em_object_mapping_list[i].element_em;
7281
7282     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7283     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7284
7285     if (em_object_mapping_list[i].action != -1)
7286       object_mapping[e].action = em_object_mapping_list[i].action;
7287
7288     if (em_object_mapping_list[i].direction != -1)
7289       object_mapping[e].direction =
7290         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7291   }
7292
7293   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7294   {
7295     int a = em_player_mapping_list[i].action_em;
7296     int p = em_player_mapping_list[i].player_nr;
7297
7298     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7299
7300     if (em_player_mapping_list[i].action != -1)
7301       player_mapping[p][a].action = em_player_mapping_list[i].action;
7302
7303     if (em_player_mapping_list[i].direction != -1)
7304       player_mapping[p][a].direction =
7305         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7306   }
7307
7308   for (i = 0; i < TILE_MAX; i++)
7309   {
7310     int element = object_mapping[i].element_rnd;
7311     int action = object_mapping[i].action;
7312     int direction = object_mapping[i].direction;
7313     boolean is_backside = object_mapping[i].is_backside;
7314     boolean action_exploding = ((action == ACTION_EXPLODING ||
7315                                  action == ACTION_SMASHED_BY_ROCK ||
7316                                  action == ACTION_SMASHED_BY_SPRING) &&
7317                                 element != EL_DIAMOND);
7318     boolean action_active = (action == ACTION_ACTIVE);
7319     boolean action_other = (action == ACTION_OTHER);
7320
7321     for (j = 0; j < 8; j++)
7322     {
7323       int effective_element = get_effective_element_EM(i, j);
7324       int effective_action = (j < 7 ? action :
7325                               i == Xdrip_stretch ? action :
7326                               i == Xdrip_stretchB ? action :
7327                               i == Ydrip_s1 ? action :
7328                               i == Ydrip_s1B ? action :
7329                               i == Xball_1B ? action :
7330                               i == Xball_2 ? action :
7331                               i == Xball_2B ? action :
7332                               i == Yball_eat ? action :
7333                               i == Ykey_1_eat ? action :
7334                               i == Ykey_2_eat ? action :
7335                               i == Ykey_3_eat ? action :
7336                               i == Ykey_4_eat ? action :
7337                               i == Ykey_5_eat ? action :
7338                               i == Ykey_6_eat ? action :
7339                               i == Ykey_7_eat ? action :
7340                               i == Ykey_8_eat ? action :
7341                               i == Ylenses_eat ? action :
7342                               i == Ymagnify_eat ? action :
7343                               i == Ygrass_eat ? action :
7344                               i == Ydirt_eat ? action :
7345                               i == Xsand_stonein_1 ? action :
7346                               i == Xsand_stonein_2 ? action :
7347                               i == Xsand_stonein_3 ? action :
7348                               i == Xsand_stonein_4 ? action :
7349                               i == Xsand_stoneout_1 ? action :
7350                               i == Xsand_stoneout_2 ? action :
7351                               i == Xboom_android ? ACTION_EXPLODING :
7352                               action_exploding ? ACTION_EXPLODING :
7353                               action_active ? action :
7354                               action_other ? action :
7355                               ACTION_DEFAULT);
7356       int graphic = (el_act_dir2img(effective_element, effective_action,
7357                                     direction));
7358       int crumbled = (el_act_dir2crm(effective_element, effective_action,
7359                                      direction));
7360       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7361       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7362       boolean has_action_graphics = (graphic != base_graphic);
7363       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7364       struct GraphicInfo *g = &graphic_info[graphic];
7365       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7366       Bitmap *src_bitmap;
7367       int src_x, src_y;
7368       /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7369       boolean special_animation = (action != ACTION_DEFAULT &&
7370                                    g->anim_frames == 3 &&
7371                                    g->anim_delay == 2 &&
7372                                    g->anim_mode & ANIM_LINEAR);
7373       int sync_frame = (i == Xdrip_stretch ? 7 :
7374                         i == Xdrip_stretchB ? 7 :
7375                         i == Ydrip_s2 ? j + 8 :
7376                         i == Ydrip_s2B ? j + 8 :
7377                         i == Xacid_1 ? 0 :
7378                         i == Xacid_2 ? 10 :
7379                         i == Xacid_3 ? 20 :
7380                         i == Xacid_4 ? 30 :
7381                         i == Xacid_5 ? 40 :
7382                         i == Xacid_6 ? 50 :
7383                         i == Xacid_7 ? 60 :
7384                         i == Xacid_8 ? 70 :
7385                         i == Xfake_acid_1 ? 0 :
7386                         i == Xfake_acid_2 ? 10 :
7387                         i == Xfake_acid_3 ? 20 :
7388                         i == Xfake_acid_4 ? 30 :
7389                         i == Xfake_acid_5 ? 40 :
7390                         i == Xfake_acid_6 ? 50 :
7391                         i == Xfake_acid_7 ? 60 :
7392                         i == Xfake_acid_8 ? 70 :
7393                         i == Xball_2 ? 7 :
7394                         i == Xball_2B ? j + 8 :
7395                         i == Yball_eat ? j + 1 :
7396                         i == Ykey_1_eat ? j + 1 :
7397                         i == Ykey_2_eat ? j + 1 :
7398                         i == Ykey_3_eat ? j + 1 :
7399                         i == Ykey_4_eat ? j + 1 :
7400                         i == Ykey_5_eat ? j + 1 :
7401                         i == Ykey_6_eat ? j + 1 :
7402                         i == Ykey_7_eat ? j + 1 :
7403                         i == Ykey_8_eat ? j + 1 :
7404                         i == Ylenses_eat ? j + 1 :
7405                         i == Ymagnify_eat ? j + 1 :
7406                         i == Ygrass_eat ? j + 1 :
7407                         i == Ydirt_eat ? j + 1 :
7408                         i == Xamoeba_1 ? 0 :
7409                         i == Xamoeba_2 ? 1 :
7410                         i == Xamoeba_3 ? 2 :
7411                         i == Xamoeba_4 ? 3 :
7412                         i == Xamoeba_5 ? 0 :
7413                         i == Xamoeba_6 ? 1 :
7414                         i == Xamoeba_7 ? 2 :
7415                         i == Xamoeba_8 ? 3 :
7416                         i == Xexit_2 ? j + 8 :
7417                         i == Xexit_3 ? j + 16 :
7418                         i == Xdynamite_1 ? 0 :
7419                         i == Xdynamite_2 ? 8 :
7420                         i == Xdynamite_3 ? 16 :
7421                         i == Xdynamite_4 ? 24 :
7422                         i == Xsand_stonein_1 ? j + 1 :
7423                         i == Xsand_stonein_2 ? j + 9 :
7424                         i == Xsand_stonein_3 ? j + 17 :
7425                         i == Xsand_stonein_4 ? j + 25 :
7426                         i == Xsand_stoneout_1 && j == 0 ? 0 :
7427                         i == Xsand_stoneout_1 && j == 1 ? 0 :
7428                         i == Xsand_stoneout_1 && j == 2 ? 1 :
7429                         i == Xsand_stoneout_1 && j == 3 ? 2 :
7430                         i == Xsand_stoneout_1 && j == 4 ? 2 :
7431                         i == Xsand_stoneout_1 && j == 5 ? 3 :
7432                         i == Xsand_stoneout_1 && j == 6 ? 4 :
7433                         i == Xsand_stoneout_1 && j == 7 ? 4 :
7434                         i == Xsand_stoneout_2 && j == 0 ? 5 :
7435                         i == Xsand_stoneout_2 && j == 1 ? 6 :
7436                         i == Xsand_stoneout_2 && j == 2 ? 7 :
7437                         i == Xsand_stoneout_2 && j == 3 ? 8 :
7438                         i == Xsand_stoneout_2 && j == 4 ? 9 :
7439                         i == Xsand_stoneout_2 && j == 5 ? 11 :
7440                         i == Xsand_stoneout_2 && j == 6 ? 13 :
7441                         i == Xsand_stoneout_2 && j == 7 ? 15 :
7442                         i == Xboom_bug && j == 1 ? 2 :
7443                         i == Xboom_bug && j == 2 ? 2 :
7444                         i == Xboom_bug && j == 3 ? 4 :
7445                         i == Xboom_bug && j == 4 ? 4 :
7446                         i == Xboom_bug && j == 5 ? 2 :
7447                         i == Xboom_bug && j == 6 ? 2 :
7448                         i == Xboom_bug && j == 7 ? 0 :
7449                         i == Xboom_bomb && j == 1 ? 2 :
7450                         i == Xboom_bomb && j == 2 ? 2 :
7451                         i == Xboom_bomb && j == 3 ? 4 :
7452                         i == Xboom_bomb && j == 4 ? 4 :
7453                         i == Xboom_bomb && j == 5 ? 2 :
7454                         i == Xboom_bomb && j == 6 ? 2 :
7455                         i == Xboom_bomb && j == 7 ? 0 :
7456                         i == Xboom_android && j == 7 ? 6 :
7457                         i == Xboom_1 && j == 1 ? 2 :
7458                         i == Xboom_1 && j == 2 ? 2 :
7459                         i == Xboom_1 && j == 3 ? 4 :
7460                         i == Xboom_1 && j == 4 ? 4 :
7461                         i == Xboom_1 && j == 5 ? 6 :
7462                         i == Xboom_1 && j == 6 ? 6 :
7463                         i == Xboom_1 && j == 7 ? 8 :
7464                         i == Xboom_2 && j == 0 ? 8 :
7465                         i == Xboom_2 && j == 1 ? 8 :
7466                         i == Xboom_2 && j == 2 ? 10 :
7467                         i == Xboom_2 && j == 3 ? 10 :
7468                         i == Xboom_2 && j == 4 ? 10 :
7469                         i == Xboom_2 && j == 5 ? 12 :
7470                         i == Xboom_2 && j == 6 ? 12 :
7471                         i == Xboom_2 && j == 7 ? 12 :
7472                         special_animation && j == 4 ? 3 :
7473                         effective_action != action ? 0 :
7474                         j);
7475
7476 #if DEBUG_EM_GFX
7477       Bitmap *debug_bitmap = g_em->bitmap;
7478       int debug_src_x = g_em->src_x;
7479       int debug_src_y = g_em->src_y;
7480 #endif
7481
7482       int frame = getAnimationFrame(g->anim_frames,
7483                                     g->anim_delay,
7484                                     g->anim_mode,
7485                                     g->anim_start_frame,
7486                                     sync_frame);
7487
7488       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7489                           g->double_movement && is_backside);
7490
7491       g_em->bitmap = src_bitmap;
7492       g_em->src_x = src_x;
7493       g_em->src_y = src_y;
7494       g_em->src_offset_x = 0;
7495       g_em->src_offset_y = 0;
7496       g_em->dst_offset_x = 0;
7497       g_em->dst_offset_y = 0;
7498       g_em->width  = TILEX;
7499       g_em->height = TILEY;
7500
7501       g_em->preserve_background = FALSE;
7502
7503       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7504                                sync_frame);
7505
7506       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7507                                    effective_action == ACTION_MOVING  ||
7508                                    effective_action == ACTION_PUSHING ||
7509                                    effective_action == ACTION_EATING)) ||
7510           (!has_action_graphics && (effective_action == ACTION_FILLING ||
7511                                     effective_action == ACTION_EMPTYING)))
7512       {
7513         int move_dir =
7514           (effective_action == ACTION_FALLING ||
7515            effective_action == ACTION_FILLING ||
7516            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7517         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7518         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
7519         int num_steps = (i == Ydrip_s1  ? 16 :
7520                          i == Ydrip_s1B ? 16 :
7521                          i == Ydrip_s2  ? 16 :
7522                          i == Ydrip_s2B ? 16 :
7523                          i == Xsand_stonein_1 ? 32 :
7524                          i == Xsand_stonein_2 ? 32 :
7525                          i == Xsand_stonein_3 ? 32 :
7526                          i == Xsand_stonein_4 ? 32 :
7527                          i == Xsand_stoneout_1 ? 16 :
7528                          i == Xsand_stoneout_2 ? 16 : 8);
7529         int cx = ABS(dx) * (TILEX / num_steps);
7530         int cy = ABS(dy) * (TILEY / num_steps);
7531         int step_frame = (i == Ydrip_s2         ? j + 8 :
7532                           i == Ydrip_s2B        ? j + 8 :
7533                           i == Xsand_stonein_2  ? j + 8 :
7534                           i == Xsand_stonein_3  ? j + 16 :
7535                           i == Xsand_stonein_4  ? j + 24 :
7536                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7537         int step = (is_backside ? step_frame : num_steps - step_frame);
7538
7539         if (is_backside)        /* tile where movement starts */
7540         {
7541           if (dx < 0 || dy < 0)
7542           {
7543             g_em->src_offset_x = cx * step;
7544             g_em->src_offset_y = cy * step;
7545           }
7546           else
7547           {
7548             g_em->dst_offset_x = cx * step;
7549             g_em->dst_offset_y = cy * step;
7550           }
7551         }
7552         else                    /* tile where movement ends */
7553         {
7554           if (dx < 0 || dy < 0)
7555           {
7556             g_em->dst_offset_x = cx * step;
7557             g_em->dst_offset_y = cy * step;
7558           }
7559           else
7560           {
7561             g_em->src_offset_x = cx * step;
7562             g_em->src_offset_y = cy * step;
7563           }
7564         }
7565
7566         g_em->width  = TILEX - cx * step;
7567         g_em->height = TILEY - cy * step;
7568       }
7569
7570       /* create unique graphic identifier to decide if tile must be redrawn */
7571       /* bit 31 - 16 (16 bit): EM style graphic
7572          bit 15 - 12 ( 4 bit): EM style frame
7573          bit 11 -  6 ( 6 bit): graphic width
7574          bit  5 -  0 ( 6 bit): graphic height */
7575       g_em->unique_identifier =
7576         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7577
7578 #if DEBUG_EM_GFX
7579
7580       /* skip check for EMC elements not contained in original EMC artwork */
7581       if (element == EL_EMC_FAKE_ACID)
7582         continue;
7583
7584       if (g_em->bitmap != debug_bitmap ||
7585           g_em->src_x != debug_src_x ||
7586           g_em->src_y != debug_src_y ||
7587           g_em->src_offset_x != 0 ||
7588           g_em->src_offset_y != 0 ||
7589           g_em->dst_offset_x != 0 ||
7590           g_em->dst_offset_y != 0 ||
7591           g_em->width != TILEX ||
7592           g_em->height != TILEY)
7593       {
7594         static int last_i = -1;
7595
7596         if (i != last_i)
7597         {
7598           printf("\n");
7599           last_i = i;
7600         }
7601
7602         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7603                i, element, element_info[element].token_name,
7604                element_action_info[effective_action].suffix, direction);
7605
7606         if (element != effective_element)
7607           printf(" [%d ('%s')]",
7608                  effective_element,
7609                  element_info[effective_element].token_name);
7610
7611         printf("\n");
7612
7613         if (g_em->bitmap != debug_bitmap)
7614           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7615                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7616
7617         if (g_em->src_x != debug_src_x ||
7618             g_em->src_y != debug_src_y)
7619           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7620                  j, (is_backside ? 'B' : 'F'),
7621                  g_em->src_x, g_em->src_y,
7622                  g_em->src_x / 32, g_em->src_y / 32,
7623                  debug_src_x, debug_src_y,
7624                  debug_src_x / 32, debug_src_y / 32);
7625
7626         if (g_em->src_offset_x != 0 ||
7627             g_em->src_offset_y != 0 ||
7628             g_em->dst_offset_x != 0 ||
7629             g_em->dst_offset_y != 0)
7630           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7631                  j, is_backside,
7632                  g_em->src_offset_x, g_em->src_offset_y,
7633                  g_em->dst_offset_x, g_em->dst_offset_y);
7634
7635         if (g_em->width != TILEX ||
7636             g_em->height != TILEY)
7637           printf("    %d (%d): size %d,%d should be %d,%d\n",
7638                  j, is_backside,
7639                  g_em->width, g_em->height, TILEX, TILEY);
7640
7641         num_em_gfx_errors++;
7642       }
7643 #endif
7644
7645     }
7646   }
7647
7648   for (i = 0; i < TILE_MAX; i++)
7649   {
7650     for (j = 0; j < 8; j++)
7651     {
7652       int element = object_mapping[i].element_rnd;
7653       int action = object_mapping[i].action;
7654       int direction = object_mapping[i].direction;
7655       boolean is_backside = object_mapping[i].is_backside;
7656       int graphic_action  = el_act_dir2img(element, action, direction);
7657       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7658
7659       if ((action == ACTION_SMASHED_BY_ROCK ||
7660            action == ACTION_SMASHED_BY_SPRING ||
7661            action == ACTION_EATING) &&
7662           graphic_action == graphic_default)
7663       {
7664         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
7665                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7666                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
7667                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7668                  Xspring);
7669
7670         /* no separate animation for "smashed by rock" -- use rock instead */
7671         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7672         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7673
7674         g_em->bitmap            = g_xx->bitmap;
7675         g_em->src_x             = g_xx->src_x;
7676         g_em->src_y             = g_xx->src_y;
7677         g_em->src_offset_x      = g_xx->src_offset_x;
7678         g_em->src_offset_y      = g_xx->src_offset_y;
7679         g_em->dst_offset_x      = g_xx->dst_offset_x;
7680         g_em->dst_offset_y      = g_xx->dst_offset_y;
7681         g_em->width             = g_xx->width;
7682         g_em->height            = g_xx->height;
7683         g_em->unique_identifier = g_xx->unique_identifier;
7684
7685         if (!is_backside)
7686           g_em->preserve_background = TRUE;
7687       }
7688     }
7689   }
7690
7691   for (p = 0; p < MAX_PLAYERS; p++)
7692   {
7693     for (i = 0; i < SPR_MAX; i++)
7694     {
7695       int element = player_mapping[p][i].element_rnd;
7696       int action = player_mapping[p][i].action;
7697       int direction = player_mapping[p][i].direction;
7698
7699       for (j = 0; j < 8; j++)
7700       {
7701         int effective_element = element;
7702         int effective_action = action;
7703         int graphic = (direction == MV_NONE ?
7704                        el_act2img(effective_element, effective_action) :
7705                        el_act_dir2img(effective_element, effective_action,
7706                                       direction));
7707         struct GraphicInfo *g = &graphic_info[graphic];
7708         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7709         Bitmap *src_bitmap;
7710         int src_x, src_y;
7711         int sync_frame = j;
7712
7713 #if DEBUG_EM_GFX
7714         Bitmap *debug_bitmap = g_em->bitmap;
7715         int debug_src_x = g_em->src_x;
7716         int debug_src_y = g_em->src_y;
7717 #endif
7718
7719         int frame = getAnimationFrame(g->anim_frames,
7720                                       g->anim_delay,
7721                                       g->anim_mode,
7722                                       g->anim_start_frame,
7723                                       sync_frame);
7724
7725         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7726
7727         g_em->bitmap = src_bitmap;
7728         g_em->src_x = src_x;
7729         g_em->src_y = src_y;
7730         g_em->src_offset_x = 0;
7731         g_em->src_offset_y = 0;
7732         g_em->dst_offset_x = 0;
7733         g_em->dst_offset_y = 0;
7734         g_em->width  = TILEX;
7735         g_em->height = TILEY;
7736
7737 #if DEBUG_EM_GFX
7738
7739         /* skip check for EMC elements not contained in original EMC artwork */
7740         if (element == EL_PLAYER_3 ||
7741             element == EL_PLAYER_4)
7742           continue;
7743
7744         if (g_em->bitmap != debug_bitmap ||
7745             g_em->src_x != debug_src_x ||
7746             g_em->src_y != debug_src_y)
7747         {
7748           static int last_i = -1;
7749
7750           if (i != last_i)
7751           {
7752             printf("\n");
7753             last_i = i;
7754           }
7755
7756           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7757                  p, i, element, element_info[element].token_name,
7758                  element_action_info[effective_action].suffix, direction);
7759
7760           if (element != effective_element)
7761             printf(" [%d ('%s')]",
7762                    effective_element,
7763                    element_info[effective_element].token_name);
7764
7765           printf("\n");
7766
7767           if (g_em->bitmap != debug_bitmap)
7768             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
7769                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
7770
7771           if (g_em->src_x != debug_src_x ||
7772               g_em->src_y != debug_src_y)
7773             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7774                    j,
7775                    g_em->src_x, g_em->src_y,
7776                    g_em->src_x / 32, g_em->src_y / 32,
7777                    debug_src_x, debug_src_y,
7778                    debug_src_x / 32, debug_src_y / 32);
7779
7780           num_em_gfx_errors++;
7781         }
7782 #endif
7783
7784       }
7785     }
7786   }
7787
7788 #if DEBUG_EM_GFX
7789   printf("\n");
7790   printf("::: [%d errors found]\n", num_em_gfx_errors);
7791
7792   exit(0);
7793 #endif
7794 }
7795
7796 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7797                             boolean any_player_moving,
7798                             boolean player_is_dropping)
7799 {
7800   if (tape.single_step && tape.recording && !tape.pausing)
7801     if (frame == 0 && !player_is_dropping)
7802       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7803 }
7804
7805 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7806                             boolean murphy_is_dropping)
7807 {
7808   if (tape.single_step && tape.recording && !tape.pausing)
7809     if (murphy_is_waiting)
7810       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7811 }
7812
7813 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7814                          int graphic, int sync_frame, int x, int y)
7815 {
7816   int frame = getGraphicAnimationFrame(graphic, sync_frame);
7817
7818   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7819 }
7820
7821 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7822 {
7823   return (IS_NEXT_FRAME(sync_frame, graphic));
7824 }
7825
7826 int getGraphicInfo_Delay(int graphic)
7827 {
7828   return graphic_info[graphic].anim_delay;
7829 }
7830
7831 void PlayMenuSoundExt(int sound)
7832 {
7833   if (sound == SND_UNDEFINED)
7834     return;
7835
7836   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7837       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7838     return;
7839
7840   if (IS_LOOP_SOUND(sound))
7841     PlaySoundLoop(sound);
7842   else
7843     PlaySound(sound);
7844 }
7845
7846 void PlayMenuSound()
7847 {
7848   PlayMenuSoundExt(menu.sound[game_status]);
7849 }
7850
7851 void PlayMenuSoundStereo(int sound, int stereo_position)
7852 {
7853   if (sound == SND_UNDEFINED)
7854     return;
7855
7856   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7857       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7858     return;
7859
7860   if (IS_LOOP_SOUND(sound))
7861     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7862   else
7863     PlaySoundStereo(sound, stereo_position);
7864 }
7865
7866 void PlayMenuSoundIfLoopExt(int sound)
7867 {
7868   if (sound == SND_UNDEFINED)
7869     return;
7870
7871   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7872       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7873     return;
7874
7875   if (IS_LOOP_SOUND(sound))
7876     PlaySoundLoop(sound);
7877 }
7878
7879 void PlayMenuSoundIfLoop()
7880 {
7881   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7882 }
7883
7884 void PlayMenuMusicExt(int music)
7885 {
7886   if (music == MUS_UNDEFINED)
7887     return;
7888
7889   if (!setup.sound_music)
7890     return;
7891
7892   PlayMusic(music);
7893 }
7894
7895 void PlayMenuMusic()
7896 {
7897   PlayMenuMusicExt(menu.music[game_status]);
7898 }
7899
7900 void PlaySoundActivating()
7901 {
7902 #if 0
7903   PlaySound(SND_MENU_ITEM_ACTIVATING);
7904 #endif
7905 }
7906
7907 void PlaySoundSelecting()
7908 {
7909 #if 0
7910   PlaySound(SND_MENU_ITEM_SELECTING);
7911 #endif
7912 }
7913
7914 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7915 {
7916   boolean change_fullscreen = (setup.fullscreen !=
7917                                video.fullscreen_enabled);
7918   boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7919                                     !strEqual(setup.fullscreen_mode,
7920                                               video.fullscreen_mode_current));
7921   boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7922                                            setup.window_scaling_percent !=
7923                                            video.window_scaling_percent);
7924
7925   if (change_window_scaling_percent && video.fullscreen_enabled)
7926     return;
7927
7928   if (!change_window_scaling_percent && !video.fullscreen_available)
7929     return;
7930
7931 #if defined(TARGET_SDL2)
7932   if (change_window_scaling_percent)
7933   {
7934     SDLSetWindowScaling(setup.window_scaling_percent);
7935
7936     return;
7937   }
7938   else if (change_fullscreen)
7939   {
7940     SDLSetWindowFullscreen(setup.fullscreen);
7941
7942     /* set setup value according to successfully changed fullscreen mode */
7943     setup.fullscreen = video.fullscreen_enabled;
7944
7945     return;
7946   }
7947 #endif
7948
7949   if (change_fullscreen ||
7950       change_fullscreen_mode ||
7951       change_window_scaling_percent)
7952   {
7953     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
7954
7955     /* save backbuffer content which gets lost when toggling fullscreen mode */
7956     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7957
7958     if (change_fullscreen_mode)
7959     {
7960       /* keep fullscreen, but change fullscreen mode (screen resolution) */
7961       video.fullscreen_enabled = FALSE;         /* force new fullscreen mode */
7962     }
7963
7964     if (change_window_scaling_percent)
7965     {
7966       /* keep window mode, but change window scaling */
7967       video.fullscreen_enabled = TRUE;          /* force new window scaling */
7968     }
7969
7970     /* toggle fullscreen */
7971     ChangeVideoModeIfNeeded(setup.fullscreen);
7972
7973     /* set setup value according to successfully changed fullscreen mode */
7974     setup.fullscreen = video.fullscreen_enabled;
7975
7976     /* restore backbuffer content from temporary backbuffer backup bitmap */
7977     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7978
7979     FreeBitmap(tmp_backbuffer);
7980
7981     /* update visible window/screen */
7982     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7983   }
7984 }
7985
7986 void ChangeViewportPropertiesIfNeeded()
7987 {
7988   int gfx_game_mode = game_status;
7989   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
7990                         game_status);
7991   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
7992   struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
7993   struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
7994   struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
7995   int border_size       = vp_playfield->border_size;
7996   int new_sx            = vp_playfield->x + border_size;
7997   int new_sy            = vp_playfield->y + border_size;
7998   int new_sxsize        = vp_playfield->width  - 2 * border_size;
7999   int new_sysize        = vp_playfield->height - 2 * border_size;
8000   int new_real_sx       = vp_playfield->x;
8001   int new_real_sy       = vp_playfield->y;
8002   int new_full_sxsize   = vp_playfield->width;
8003   int new_full_sysize   = vp_playfield->height;
8004   int new_dx            = vp_door_1->x;
8005   int new_dy            = vp_door_1->y;
8006   int new_dxsize        = vp_door_1->width;
8007   int new_dysize        = vp_door_1->height;
8008   int new_vx            = vp_door_2->x;
8009   int new_vy            = vp_door_2->y;
8010   int new_vxsize        = vp_door_2->width;
8011   int new_vysize        = vp_door_2->height;
8012   int new_ex            = vp_door_3->x;
8013   int new_ey            = vp_door_3->y;
8014   int new_exsize        = vp_door_3->width;
8015   int new_eysize        = vp_door_3->height;
8016   int new_tilesize_var =
8017     (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8018
8019   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8020                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8021   int new_scr_fieldx = new_sxsize / tilesize;
8022   int new_scr_fieldy = new_sysize / tilesize;
8023   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8024   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8025   boolean init_gfx_buffers = FALSE;
8026   boolean init_video_buffer = FALSE;
8027   boolean init_gadgets_and_toons = FALSE;
8028   boolean init_em_graphics = FALSE;
8029   boolean drawing_area_changed = FALSE;
8030
8031   if (viewport.window.width  != WIN_XSIZE ||
8032       viewport.window.height != WIN_YSIZE)
8033   {
8034     WIN_XSIZE = viewport.window.width;
8035     WIN_YSIZE = viewport.window.height;
8036
8037     init_video_buffer = TRUE;
8038     init_gfx_buffers = TRUE;
8039
8040     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8041   }
8042
8043   if (new_scr_fieldx != SCR_FIELDX ||
8044       new_scr_fieldy != SCR_FIELDY)
8045   {
8046     /* this always toggles between MAIN and GAME when using small tile size */
8047
8048     SCR_FIELDX = new_scr_fieldx;
8049     SCR_FIELDY = new_scr_fieldy;
8050
8051     // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8052   }
8053
8054   if (new_sx != SX ||
8055       new_sy != SY ||
8056       new_dx != DX ||
8057       new_dy != DY ||
8058       new_vx != VX ||
8059       new_vy != VY ||
8060       new_ex != EX ||
8061       new_ey != EY ||
8062       new_sxsize != SXSIZE ||
8063       new_sysize != SYSIZE ||
8064       new_dxsize != DXSIZE ||
8065       new_dysize != DYSIZE ||
8066       new_vxsize != VXSIZE ||
8067       new_vysize != VYSIZE ||
8068       new_exsize != EXSIZE ||
8069       new_eysize != EYSIZE ||
8070       new_real_sx != REAL_SX ||
8071       new_real_sy != REAL_SY ||
8072       new_full_sxsize != FULL_SXSIZE ||
8073       new_full_sysize != FULL_SYSIZE ||
8074       new_tilesize_var != TILESIZE_VAR
8075       )
8076   {
8077     if (new_tilesize_var != TILESIZE_VAR)
8078     {
8079       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8080
8081       // changing tile size invalidates scroll values of engine snapshots
8082       FreeEngineSnapshot();
8083
8084       // changing tile size requires update of graphic mapping for EM engine
8085       init_em_graphics = TRUE;
8086     }
8087
8088     if (new_sx != SX ||
8089         new_sy != SY ||
8090         new_sxsize != SXSIZE ||
8091         new_sysize != SYSIZE ||
8092         new_real_sx != REAL_SX ||
8093         new_real_sy != REAL_SY ||
8094         new_full_sxsize != FULL_SXSIZE ||
8095         new_full_sysize != FULL_SYSIZE)
8096     {
8097       if (!init_video_buffer)
8098         drawing_area_changed = TRUE;
8099     }
8100
8101     SX = new_sx;
8102     SY = new_sy;
8103     DX = new_dx;
8104     DY = new_dy;
8105     VX = new_vx;
8106     VY = new_vy;
8107     EX = new_ex;
8108     EY = new_ey;
8109     SXSIZE = new_sxsize;
8110     SYSIZE = new_sysize;
8111     DXSIZE = new_dxsize;
8112     DYSIZE = new_dysize;
8113     VXSIZE = new_vxsize;
8114     VYSIZE = new_vysize;
8115     EXSIZE = new_exsize;
8116     EYSIZE = new_eysize;
8117     REAL_SX = new_real_sx;
8118     REAL_SY = new_real_sy;
8119     FULL_SXSIZE = new_full_sxsize;
8120     FULL_SYSIZE = new_full_sysize;
8121     TILESIZE_VAR = new_tilesize_var;
8122
8123     init_gfx_buffers = TRUE;
8124     init_gadgets_and_toons = TRUE;
8125
8126     // printf("::: viewports: init_gfx_buffers\n");
8127     // printf("::: viewports: init_gadgets_and_toons\n");
8128   }
8129
8130   if (init_gfx_buffers)
8131   {
8132     // printf("::: init_gfx_buffers\n");
8133
8134     SCR_FIELDX = new_scr_fieldx_buffers;
8135     SCR_FIELDY = new_scr_fieldy_buffers;
8136
8137     InitGfxBuffers();
8138
8139     SCR_FIELDX = new_scr_fieldx;
8140     SCR_FIELDY = new_scr_fieldy;
8141
8142     gfx.drawing_area_changed = drawing_area_changed;
8143
8144     SetDrawDeactivationMask(REDRAW_NONE);
8145     SetDrawBackgroundMask(REDRAW_FIELD);
8146   }
8147
8148   if (init_video_buffer)
8149   {
8150     // printf("::: init_video_buffer\n");
8151
8152     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8153   }
8154
8155   if (init_gadgets_and_toons)
8156   {
8157     // printf("::: init_gadgets_and_toons\n");
8158
8159     InitGadgets();
8160     InitToons();
8161   }
8162
8163   if (init_em_graphics)
8164   {
8165       InitGraphicInfo_EM();
8166   }
8167 }