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