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