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