added fix to prevent Windows from complaining about program not responding
[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         /* prevent OS (Windows) from complaining about program not responding */
4871         CheckQuitEvent();
4872       }
4873
4874       if (door_part_done_all)
4875         break;
4876     }
4877   }
4878
4879   if (door_state & DOOR_ACTION_1)
4880     door1 = door_state & DOOR_ACTION_1;
4881   if (door_state & DOOR_ACTION_2)
4882     door2 = door_state & DOOR_ACTION_2;
4883
4884   // draw masked border over door area
4885   DrawMaskedBorder(REDRAW_DOOR_1);
4886   DrawMaskedBorder(REDRAW_DOOR_2);
4887
4888   return (door1 | door2);
4889 }
4890
4891 static boolean useSpecialEditorDoor()
4892 {
4893   int graphic = IMG_GLOBAL_BORDER_EDITOR;
4894   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4895
4896   // do not draw special editor door if editor border defined or redefined
4897   if (graphic_info[graphic].bitmap != NULL || redefined)
4898     return FALSE;
4899
4900   // do not draw special editor door if global border defined to be empty
4901   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4902     return FALSE;
4903
4904   // do not draw special editor door if viewport definitions do not match
4905   if (EX != VX ||
4906       EY >= VY ||
4907       EXSIZE != VXSIZE ||
4908       EY + EYSIZE != VY + VYSIZE)
4909     return FALSE;
4910
4911   return TRUE;
4912 }
4913
4914 void DrawSpecialEditorDoor()
4915 {
4916   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4917   int top_border_width = gfx1->width;
4918   int top_border_height = gfx1->height;
4919   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4920   int ex = EX - outer_border;
4921   int ey = EY - outer_border;
4922   int vy = VY - outer_border;
4923   int exsize = EXSIZE + 2 * outer_border;
4924
4925   if (!useSpecialEditorDoor())
4926     return;
4927
4928   /* draw bigger level editor toolbox window */
4929   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4930              top_border_width, top_border_height, ex, ey - top_border_height);
4931   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4932              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4933
4934   redraw_mask |= REDRAW_ALL;
4935 }
4936
4937 void UndrawSpecialEditorDoor()
4938 {
4939   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4940   int top_border_width = gfx1->width;
4941   int top_border_height = gfx1->height;
4942   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4943   int ex = EX - outer_border;
4944   int ey = EY - outer_border;
4945   int ey_top = ey - top_border_height;
4946   int exsize = EXSIZE + 2 * outer_border;
4947   int eysize = EYSIZE + 2 * outer_border;
4948
4949   if (!useSpecialEditorDoor())
4950     return;
4951
4952   /* draw normal tape recorder window */
4953   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4954   {
4955     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4956                ex, ey_top, top_border_width, top_border_height,
4957                ex, ey_top);
4958     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4959                ex, ey, exsize, eysize, ex, ey);
4960   }
4961   else
4962   {
4963     // if screen background is set to "[NONE]", clear editor toolbox window
4964     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4965     ClearRectangle(drawto, ex, ey, exsize, eysize);
4966   }
4967
4968   redraw_mask |= REDRAW_ALL;
4969 }
4970
4971
4972 /* ---------- new tool button stuff ---------------------------------------- */
4973
4974 static struct
4975 {
4976   int graphic;
4977   struct TextPosInfo *pos;
4978   int gadget_id;
4979   char *infotext;
4980 } toolbutton_info[NUM_TOOL_BUTTONS] =
4981 {
4982   {
4983     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
4984     TOOL_CTRL_ID_YES,                   "yes"
4985   },
4986   {
4987     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
4988     TOOL_CTRL_ID_NO,                    "no"
4989   },
4990   {
4991     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
4992     TOOL_CTRL_ID_CONFIRM,               "confirm"
4993   },
4994   {
4995     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
4996     TOOL_CTRL_ID_PLAYER_1,              "player 1"
4997   },
4998   {
4999     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5000     TOOL_CTRL_ID_PLAYER_2,              "player 2"
5001   },
5002   {
5003     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5004     TOOL_CTRL_ID_PLAYER_3,              "player 3"
5005   },
5006   {
5007     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5008     TOOL_CTRL_ID_PLAYER_4,              "player 4"
5009   }
5010 };
5011
5012 void CreateToolButtons()
5013 {
5014   int i;
5015
5016   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5017   {
5018     struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5019     struct TextPosInfo *pos = toolbutton_info[i].pos;
5020     struct GadgetInfo *gi;
5021     Bitmap *deco_bitmap = None;
5022     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5023     unsigned int event_mask = GD_EVENT_RELEASED;
5024     int dx = DX;
5025     int dy = DY;
5026     int gd_x = gfx->src_x;
5027     int gd_y = gfx->src_y;
5028     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5029     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5030     int id = i;
5031
5032     if (global.use_envelope_request)
5033       setRequestPosition(&dx, &dy, TRUE);
5034
5035     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5036     {
5037       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5038
5039       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5040                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5041       deco_xpos = (gfx->width  - pos->size) / 2;
5042       deco_ypos = (gfx->height - pos->size) / 2;
5043     }
5044
5045     gi = CreateGadget(GDI_CUSTOM_ID, id,
5046                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5047                       GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5048                       GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5049                       GDI_WIDTH, gfx->width,
5050                       GDI_HEIGHT, gfx->height,
5051                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5052                       GDI_STATE, GD_BUTTON_UNPRESSED,
5053                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5054                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5055                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5056                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5057                       GDI_DECORATION_SIZE, pos->size, pos->size,
5058                       GDI_DECORATION_SHIFTING, 1, 1,
5059                       GDI_DIRECT_DRAW, FALSE,
5060                       GDI_EVENT_MASK, event_mask,
5061                       GDI_CALLBACK_ACTION, HandleToolButtons,
5062                       GDI_END);
5063
5064     if (gi == NULL)
5065       Error(ERR_EXIT, "cannot create gadget");
5066
5067     tool_gadget[id] = gi;
5068   }
5069 }
5070
5071 void FreeToolButtons()
5072 {
5073   int i;
5074
5075   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5076     FreeGadget(tool_gadget[i]);
5077 }
5078
5079 static void UnmapToolButtons()
5080 {
5081   int i;
5082
5083   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5084     UnmapGadget(tool_gadget[i]);
5085 }
5086
5087 static void HandleToolButtons(struct GadgetInfo *gi)
5088 {
5089   request_gadget_id = gi->custom_id;
5090 }
5091
5092 static struct Mapping_EM_to_RND_object
5093 {
5094   int element_em;
5095   boolean is_rnd_to_em_mapping;         /* unique mapping EM <-> RND */
5096   boolean is_backside;                  /* backside of moving element */
5097
5098   int element_rnd;
5099   int action;
5100   int direction;
5101 }
5102 em_object_mapping_list[] =
5103 {
5104   {
5105     Xblank,                             TRUE,   FALSE,
5106     EL_EMPTY,                           -1, -1
5107   },
5108   {
5109     Yacid_splash_eB,                    FALSE,  FALSE,
5110     EL_ACID_SPLASH_RIGHT,               -1, -1
5111   },
5112   {
5113     Yacid_splash_wB,                    FALSE,  FALSE,
5114     EL_ACID_SPLASH_LEFT,                -1, -1
5115   },
5116
5117 #ifdef EM_ENGINE_BAD_ROLL
5118   {
5119     Xstone_force_e,                     FALSE,  FALSE,
5120     EL_ROCK,                            -1, MV_BIT_RIGHT
5121   },
5122   {
5123     Xstone_force_w,                     FALSE,  FALSE,
5124     EL_ROCK,                            -1, MV_BIT_LEFT
5125   },
5126   {
5127     Xnut_force_e,                       FALSE,  FALSE,
5128     EL_NUT,                             -1, MV_BIT_RIGHT
5129   },
5130   {
5131     Xnut_force_w,                       FALSE,  FALSE,
5132     EL_NUT,                             -1, MV_BIT_LEFT
5133   },
5134   {
5135     Xspring_force_e,                    FALSE,  FALSE,
5136     EL_SPRING,                          -1, MV_BIT_RIGHT
5137   },
5138   {
5139     Xspring_force_w,                    FALSE,  FALSE,
5140     EL_SPRING,                          -1, MV_BIT_LEFT
5141   },
5142   {
5143     Xemerald_force_e,                   FALSE,  FALSE,
5144     EL_EMERALD,                         -1, MV_BIT_RIGHT
5145   },
5146   {
5147     Xemerald_force_w,                   FALSE,  FALSE,
5148     EL_EMERALD,                         -1, MV_BIT_LEFT
5149   },
5150   {
5151     Xdiamond_force_e,                   FALSE,  FALSE,
5152     EL_DIAMOND,                         -1, MV_BIT_RIGHT
5153   },
5154   {
5155     Xdiamond_force_w,                   FALSE,  FALSE,
5156     EL_DIAMOND,                         -1, MV_BIT_LEFT
5157   },
5158   {
5159     Xbomb_force_e,                      FALSE,  FALSE,
5160     EL_BOMB,                            -1, MV_BIT_RIGHT
5161   },
5162   {
5163     Xbomb_force_w,                      FALSE,  FALSE,
5164     EL_BOMB,                            -1, MV_BIT_LEFT
5165   },
5166 #endif  /* EM_ENGINE_BAD_ROLL */
5167
5168   {
5169     Xstone,                             TRUE,   FALSE,
5170     EL_ROCK,                            -1, -1
5171   },
5172   {
5173     Xstone_pause,                       FALSE,  FALSE,
5174     EL_ROCK,                            -1, -1
5175   },
5176   {
5177     Xstone_fall,                        FALSE,  FALSE,
5178     EL_ROCK,                            -1, -1
5179   },
5180   {
5181     Ystone_s,                           FALSE,  FALSE,
5182     EL_ROCK,                            ACTION_FALLING, -1
5183   },
5184   {
5185     Ystone_sB,                          FALSE,  TRUE,
5186     EL_ROCK,                            ACTION_FALLING, -1
5187   },
5188   {
5189     Ystone_e,                           FALSE,  FALSE,
5190     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5191   },
5192   {
5193     Ystone_eB,                          FALSE,  TRUE,
5194     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5195   },
5196   {
5197     Ystone_w,                           FALSE,  FALSE,
5198     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5199   },
5200   {
5201     Ystone_wB,                          FALSE,  TRUE,
5202     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5203   },
5204   {
5205     Xnut,                               TRUE,   FALSE,
5206     EL_NUT,                             -1, -1
5207   },
5208   {
5209     Xnut_pause,                         FALSE,  FALSE,
5210     EL_NUT,                             -1, -1
5211   },
5212   {
5213     Xnut_fall,                          FALSE,  FALSE,
5214     EL_NUT,                             -1, -1
5215   },
5216   {
5217     Ynut_s,                             FALSE,  FALSE,
5218     EL_NUT,                             ACTION_FALLING, -1
5219   },
5220   {
5221     Ynut_sB,                            FALSE,  TRUE,
5222     EL_NUT,                             ACTION_FALLING, -1
5223   },
5224   {
5225     Ynut_e,                             FALSE,  FALSE,
5226     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5227   },
5228   {
5229     Ynut_eB,                            FALSE,  TRUE,
5230     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5231   },
5232   {
5233     Ynut_w,                             FALSE,  FALSE,
5234     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5235   },
5236   {
5237     Ynut_wB,                            FALSE,  TRUE,
5238     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5239   },
5240   {
5241     Xbug_n,                             TRUE,   FALSE,
5242     EL_BUG_UP,                          -1, -1
5243   },
5244   {
5245     Xbug_e,                             TRUE,   FALSE,
5246     EL_BUG_RIGHT,                       -1, -1
5247   },
5248   {
5249     Xbug_s,                             TRUE,   FALSE,
5250     EL_BUG_DOWN,                        -1, -1
5251   },
5252   {
5253     Xbug_w,                             TRUE,   FALSE,
5254     EL_BUG_LEFT,                        -1, -1
5255   },
5256   {
5257     Xbug_gon,                           FALSE,  FALSE,
5258     EL_BUG_UP,                          -1, -1
5259   },
5260   {
5261     Xbug_goe,                           FALSE,  FALSE,
5262     EL_BUG_RIGHT,                       -1, -1
5263   },
5264   {
5265     Xbug_gos,                           FALSE,  FALSE,
5266     EL_BUG_DOWN,                        -1, -1
5267   },
5268   {
5269     Xbug_gow,                           FALSE,  FALSE,
5270     EL_BUG_LEFT,                        -1, -1
5271   },
5272   {
5273     Ybug_n,                             FALSE,  FALSE,
5274     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5275   },
5276   {
5277     Ybug_nB,                            FALSE,  TRUE,
5278     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5279   },
5280   {
5281     Ybug_e,                             FALSE,  FALSE,
5282     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5283   },
5284   {
5285     Ybug_eB,                            FALSE,  TRUE,
5286     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5287   },
5288   {
5289     Ybug_s,                             FALSE,  FALSE,
5290     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5291   },
5292   {
5293     Ybug_sB,                            FALSE,  TRUE,
5294     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5295   },
5296   {
5297     Ybug_w,                             FALSE,  FALSE,
5298     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5299   },
5300   {
5301     Ybug_wB,                            FALSE,  TRUE,
5302     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5303   },
5304   {
5305     Ybug_w_n,                           FALSE,  FALSE,
5306     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5307   },
5308   {
5309     Ybug_n_e,                           FALSE,  FALSE,
5310     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5311   },
5312   {
5313     Ybug_e_s,                           FALSE,  FALSE,
5314     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5315   },
5316   {
5317     Ybug_s_w,                           FALSE,  FALSE,
5318     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5319   },
5320   {
5321     Ybug_e_n,                           FALSE,  FALSE,
5322     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5323   },
5324   {
5325     Ybug_s_e,                           FALSE,  FALSE,
5326     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5327   },
5328   {
5329     Ybug_w_s,                           FALSE,  FALSE,
5330     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5331   },
5332   {
5333     Ybug_n_w,                           FALSE,  FALSE,
5334     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5335   },
5336   {
5337     Ybug_stone,                         FALSE,  FALSE,
5338     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
5339   },
5340   {
5341     Ybug_spring,                        FALSE,  FALSE,
5342     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
5343   },
5344   {
5345     Xtank_n,                            TRUE,   FALSE,
5346     EL_SPACESHIP_UP,                    -1, -1
5347   },
5348   {
5349     Xtank_e,                            TRUE,   FALSE,
5350     EL_SPACESHIP_RIGHT,                 -1, -1
5351   },
5352   {
5353     Xtank_s,                            TRUE,   FALSE,
5354     EL_SPACESHIP_DOWN,                  -1, -1
5355   },
5356   {
5357     Xtank_w,                            TRUE,   FALSE,
5358     EL_SPACESHIP_LEFT,                  -1, -1
5359   },
5360   {
5361     Xtank_gon,                          FALSE,  FALSE,
5362     EL_SPACESHIP_UP,                    -1, -1
5363   },
5364   {
5365     Xtank_goe,                          FALSE,  FALSE,
5366     EL_SPACESHIP_RIGHT,                 -1, -1
5367   },
5368   {
5369     Xtank_gos,                          FALSE,  FALSE,
5370     EL_SPACESHIP_DOWN,                  -1, -1
5371   },
5372   {
5373     Xtank_gow,                          FALSE,  FALSE,
5374     EL_SPACESHIP_LEFT,                  -1, -1
5375   },
5376   {
5377     Ytank_n,                            FALSE,  FALSE,
5378     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5379   },
5380   {
5381     Ytank_nB,                           FALSE,  TRUE,
5382     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5383   },
5384   {
5385     Ytank_e,                            FALSE,  FALSE,
5386     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5387   },
5388   {
5389     Ytank_eB,                           FALSE,  TRUE,
5390     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5391   },
5392   {
5393     Ytank_s,                            FALSE,  FALSE,
5394     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5395   },
5396   {
5397     Ytank_sB,                           FALSE,  TRUE,
5398     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5399   },
5400   {
5401     Ytank_w,                            FALSE,  FALSE,
5402     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5403   },
5404   {
5405     Ytank_wB,                           FALSE,  TRUE,
5406     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5407   },
5408   {
5409     Ytank_w_n,                          FALSE,  FALSE,
5410     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5411   },
5412   {
5413     Ytank_n_e,                          FALSE,  FALSE,
5414     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5415   },
5416   {
5417     Ytank_e_s,                          FALSE,  FALSE,
5418     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5419   },
5420   {
5421     Ytank_s_w,                          FALSE,  FALSE,
5422     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5423   },
5424   {
5425     Ytank_e_n,                          FALSE,  FALSE,
5426     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5427   },
5428   {
5429     Ytank_s_e,                          FALSE,  FALSE,
5430     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5431   },
5432   {
5433     Ytank_w_s,                          FALSE,  FALSE,
5434     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5435   },
5436   {
5437     Ytank_n_w,                          FALSE,  FALSE,
5438     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5439   },
5440   {
5441     Ytank_stone,                        FALSE,  FALSE,
5442     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
5443   },
5444   {
5445     Ytank_spring,                       FALSE,  FALSE,
5446     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
5447   },
5448   {
5449     Xandroid,                           TRUE,   FALSE,
5450     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
5451   },
5452   {
5453     Xandroid_1_n,                       FALSE,  FALSE,
5454     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5455   },
5456   {
5457     Xandroid_2_n,                       FALSE,  FALSE,
5458     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5459   },
5460   {
5461     Xandroid_1_e,                       FALSE,  FALSE,
5462     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5463   },
5464   {
5465     Xandroid_2_e,                       FALSE,  FALSE,
5466     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5467   },
5468   {
5469     Xandroid_1_w,                       FALSE,  FALSE,
5470     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5471   },
5472   {
5473     Xandroid_2_w,                       FALSE,  FALSE,
5474     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5475   },
5476   {
5477     Xandroid_1_s,                       FALSE,  FALSE,
5478     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5479   },
5480   {
5481     Xandroid_2_s,                       FALSE,  FALSE,
5482     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5483   },
5484   {
5485     Yandroid_n,                         FALSE,  FALSE,
5486     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5487   },
5488   {
5489     Yandroid_nB,                        FALSE,  TRUE,
5490     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5491   },
5492   {
5493     Yandroid_ne,                        FALSE,  FALSE,
5494     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
5495   },
5496   {
5497     Yandroid_neB,                       FALSE,  TRUE,
5498     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
5499   },
5500   {
5501     Yandroid_e,                         FALSE,  FALSE,
5502     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5503   },
5504   {
5505     Yandroid_eB,                        FALSE,  TRUE,
5506     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5507   },
5508   {
5509     Yandroid_se,                        FALSE,  FALSE,
5510     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
5511   },
5512   {
5513     Yandroid_seB,                       FALSE,  TRUE,
5514     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5515   },
5516   {
5517     Yandroid_s,                         FALSE,  FALSE,
5518     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5519   },
5520   {
5521     Yandroid_sB,                        FALSE,  TRUE,
5522     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5523   },
5524   {
5525     Yandroid_sw,                        FALSE,  FALSE,
5526     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
5527   },
5528   {
5529     Yandroid_swB,                       FALSE,  TRUE,
5530     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
5531   },
5532   {
5533     Yandroid_w,                         FALSE,  FALSE,
5534     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5535   },
5536   {
5537     Yandroid_wB,                        FALSE,  TRUE,
5538     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5539   },
5540   {
5541     Yandroid_nw,                        FALSE,  FALSE,
5542     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
5543   },
5544   {
5545     Yandroid_nwB,                       FALSE,  TRUE,
5546     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
5547   },
5548   {
5549     Xspring,                            TRUE,   FALSE,
5550     EL_SPRING,                          -1, -1
5551   },
5552   {
5553     Xspring_pause,                      FALSE,  FALSE,
5554     EL_SPRING,                          -1, -1
5555   },
5556   {
5557     Xspring_e,                          FALSE,  FALSE,
5558     EL_SPRING,                          -1, -1
5559   },
5560   {
5561     Xspring_w,                          FALSE,  FALSE,
5562     EL_SPRING,                          -1, -1
5563   },
5564   {
5565     Xspring_fall,                       FALSE,  FALSE,
5566     EL_SPRING,                          -1, -1
5567   },
5568   {
5569     Yspring_s,                          FALSE,  FALSE,
5570     EL_SPRING,                          ACTION_FALLING, -1
5571   },
5572   {
5573     Yspring_sB,                         FALSE,  TRUE,
5574     EL_SPRING,                          ACTION_FALLING, -1
5575   },
5576   {
5577     Yspring_e,                          FALSE,  FALSE,
5578     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5579   },
5580   {
5581     Yspring_eB,                         FALSE,  TRUE,
5582     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5583   },
5584   {
5585     Yspring_w,                          FALSE,  FALSE,
5586     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5587   },
5588   {
5589     Yspring_wB,                         FALSE,  TRUE,
5590     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5591   },
5592   {
5593     Yspring_kill_e,                     FALSE,  FALSE,
5594     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5595   },
5596   {
5597     Yspring_kill_eB,                    FALSE,  TRUE,
5598     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5599   },
5600   {
5601     Yspring_kill_w,                     FALSE,  FALSE,
5602     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5603   },
5604   {
5605     Yspring_kill_wB,                    FALSE,  TRUE,
5606     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5607   },
5608   {
5609     Xeater_n,                           TRUE,   FALSE,
5610     EL_YAMYAM_UP,                       -1, -1
5611   },
5612   {
5613     Xeater_e,                           TRUE,   FALSE,
5614     EL_YAMYAM_RIGHT,                    -1, -1
5615   },
5616   {
5617     Xeater_w,                           TRUE,   FALSE,
5618     EL_YAMYAM_LEFT,                     -1, -1
5619   },
5620   {
5621     Xeater_s,                           TRUE,   FALSE,
5622     EL_YAMYAM_DOWN,                     -1, -1
5623   },
5624   {
5625     Yeater_n,                           FALSE,  FALSE,
5626     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5627   },
5628   {
5629     Yeater_nB,                          FALSE,  TRUE,
5630     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5631   },
5632   {
5633     Yeater_e,                           FALSE,  FALSE,
5634     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5635   },
5636   {
5637     Yeater_eB,                          FALSE,  TRUE,
5638     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5639   },
5640   {
5641     Yeater_s,                           FALSE,  FALSE,
5642     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5643   },
5644   {
5645     Yeater_sB,                          FALSE,  TRUE,
5646     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5647   },
5648   {
5649     Yeater_w,                           FALSE,  FALSE,
5650     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5651   },
5652   {
5653     Yeater_wB,                          FALSE,  TRUE,
5654     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5655   },
5656   {
5657     Yeater_stone,                       FALSE,  FALSE,
5658     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
5659   },
5660   {
5661     Yeater_spring,                      FALSE,  FALSE,
5662     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
5663   },
5664   {
5665     Xalien,                             TRUE,   FALSE,
5666     EL_ROBOT,                           -1, -1
5667   },
5668   {
5669     Xalien_pause,                       FALSE,  FALSE,
5670     EL_ROBOT,                           -1, -1
5671   },
5672   {
5673     Yalien_n,                           FALSE,  FALSE,
5674     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5675   },
5676   {
5677     Yalien_nB,                          FALSE,  TRUE,
5678     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5679   },
5680   {
5681     Yalien_e,                           FALSE,  FALSE,
5682     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5683   },
5684   {
5685     Yalien_eB,                          FALSE,  TRUE,
5686     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5687   },
5688   {
5689     Yalien_s,                           FALSE,  FALSE,
5690     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5691   },
5692   {
5693     Yalien_sB,                          FALSE,  TRUE,
5694     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5695   },
5696   {
5697     Yalien_w,                           FALSE,  FALSE,
5698     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5699   },
5700   {
5701     Yalien_wB,                          FALSE,  TRUE,
5702     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5703   },
5704   {
5705     Yalien_stone,                       FALSE,  FALSE,
5706     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
5707   },
5708   {
5709     Yalien_spring,                      FALSE,  FALSE,
5710     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
5711   },
5712   {
5713     Xemerald,                           TRUE,   FALSE,
5714     EL_EMERALD,                         -1, -1
5715   },
5716   {
5717     Xemerald_pause,                     FALSE,  FALSE,
5718     EL_EMERALD,                         -1, -1
5719   },
5720   {
5721     Xemerald_fall,                      FALSE,  FALSE,
5722     EL_EMERALD,                         -1, -1
5723   },
5724   {
5725     Xemerald_shine,                     FALSE,  FALSE,
5726     EL_EMERALD,                         ACTION_TWINKLING, -1
5727   },
5728   {
5729     Yemerald_s,                         FALSE,  FALSE,
5730     EL_EMERALD,                         ACTION_FALLING, -1
5731   },
5732   {
5733     Yemerald_sB,                        FALSE,  TRUE,
5734     EL_EMERALD,                         ACTION_FALLING, -1
5735   },
5736   {
5737     Yemerald_e,                         FALSE,  FALSE,
5738     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5739   },
5740   {
5741     Yemerald_eB,                        FALSE,  TRUE,
5742     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5743   },
5744   {
5745     Yemerald_w,                         FALSE,  FALSE,
5746     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5747   },
5748   {
5749     Yemerald_wB,                        FALSE,  TRUE,
5750     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5751   },
5752   {
5753     Yemerald_eat,                       FALSE,  FALSE,
5754     EL_EMERALD,                         ACTION_COLLECTING, -1
5755   },
5756   {
5757     Yemerald_stone,                     FALSE,  FALSE,
5758     EL_NUT,                             ACTION_BREAKING, -1
5759   },
5760   {
5761     Xdiamond,                           TRUE,   FALSE,
5762     EL_DIAMOND,                         -1, -1
5763   },
5764   {
5765     Xdiamond_pause,                     FALSE,  FALSE,
5766     EL_DIAMOND,                         -1, -1
5767   },
5768   {
5769     Xdiamond_fall,                      FALSE,  FALSE,
5770     EL_DIAMOND,                         -1, -1
5771   },
5772   {
5773     Xdiamond_shine,                     FALSE,  FALSE,
5774     EL_DIAMOND,                         ACTION_TWINKLING, -1
5775   },
5776   {
5777     Ydiamond_s,                         FALSE,  FALSE,
5778     EL_DIAMOND,                         ACTION_FALLING, -1
5779   },
5780   {
5781     Ydiamond_sB,                        FALSE,  TRUE,
5782     EL_DIAMOND,                         ACTION_FALLING, -1
5783   },
5784   {
5785     Ydiamond_e,                         FALSE,  FALSE,
5786     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5787   },
5788   {
5789     Ydiamond_eB,                        FALSE,  TRUE,
5790     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5791   },
5792   {
5793     Ydiamond_w,                         FALSE,  FALSE,
5794     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5795   },
5796   {
5797     Ydiamond_wB,                        FALSE,  TRUE,
5798     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5799   },
5800   {
5801     Ydiamond_eat,                       FALSE,  FALSE,
5802     EL_DIAMOND,                         ACTION_COLLECTING, -1
5803   },
5804   {
5805     Ydiamond_stone,                     FALSE,  FALSE,
5806     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
5807   },
5808   {
5809     Xdrip_fall,                         TRUE,   FALSE,
5810     EL_AMOEBA_DROP,                     -1, -1
5811   },
5812   {
5813     Xdrip_stretch,                      FALSE,  FALSE,
5814     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5815   },
5816   {
5817     Xdrip_stretchB,                     FALSE,  TRUE,
5818     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5819   },
5820   {
5821     Xdrip_eat,                          FALSE,  FALSE,
5822     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
5823   },
5824   {
5825     Ydrip_s1,                           FALSE,  FALSE,
5826     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5827   },
5828   {
5829     Ydrip_s1B,                          FALSE,  TRUE,
5830     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5831   },
5832   {
5833     Ydrip_s2,                           FALSE,  FALSE,
5834     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5835   },
5836   {
5837     Ydrip_s2B,                          FALSE,  TRUE,
5838     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5839   },
5840   {
5841     Xbomb,                              TRUE,   FALSE,
5842     EL_BOMB,                            -1, -1
5843   },
5844   {
5845     Xbomb_pause,                        FALSE,  FALSE,
5846     EL_BOMB,                            -1, -1
5847   },
5848   {
5849     Xbomb_fall,                         FALSE,  FALSE,
5850     EL_BOMB,                            -1, -1
5851   },
5852   {
5853     Ybomb_s,                            FALSE,  FALSE,
5854     EL_BOMB,                            ACTION_FALLING, -1
5855   },
5856   {
5857     Ybomb_sB,                           FALSE,  TRUE,
5858     EL_BOMB,                            ACTION_FALLING, -1
5859   },
5860   {
5861     Ybomb_e,                            FALSE,  FALSE,
5862     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5863   },
5864   {
5865     Ybomb_eB,                           FALSE,  TRUE,
5866     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5867   },
5868   {
5869     Ybomb_w,                            FALSE,  FALSE,
5870     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5871   },
5872   {
5873     Ybomb_wB,                           FALSE,  TRUE,
5874     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5875   },
5876   {
5877     Ybomb_eat,                          FALSE,  FALSE,
5878     EL_BOMB,                            ACTION_ACTIVATING, -1
5879   },
5880   {
5881     Xballoon,                           TRUE,   FALSE,
5882     EL_BALLOON,                         -1, -1
5883   },
5884   {
5885     Yballoon_n,                         FALSE,  FALSE,
5886     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5887   },
5888   {
5889     Yballoon_nB,                        FALSE,  TRUE,
5890     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5891   },
5892   {
5893     Yballoon_e,                         FALSE,  FALSE,
5894     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5895   },
5896   {
5897     Yballoon_eB,                        FALSE,  TRUE,
5898     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5899   },
5900   {
5901     Yballoon_s,                         FALSE,  FALSE,
5902     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5903   },
5904   {
5905     Yballoon_sB,                        FALSE,  TRUE,
5906     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5907   },
5908   {
5909     Yballoon_w,                         FALSE,  FALSE,
5910     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5911   },
5912   {
5913     Yballoon_wB,                        FALSE,  TRUE,
5914     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5915   },
5916   {
5917     Xgrass,                             TRUE,   FALSE,
5918     EL_EMC_GRASS,                       -1, -1
5919   },
5920   {
5921     Ygrass_nB,                          FALSE,  FALSE,
5922     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
5923   },
5924   {
5925     Ygrass_eB,                          FALSE,  FALSE,
5926     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
5927   },
5928   {
5929     Ygrass_sB,                          FALSE,  FALSE,
5930     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
5931   },
5932   {
5933     Ygrass_wB,                          FALSE,  FALSE,
5934     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
5935   },
5936   {
5937     Xdirt,                              TRUE,   FALSE,
5938     EL_SAND,                            -1, -1
5939   },
5940   {
5941     Ydirt_nB,                           FALSE,  FALSE,
5942     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
5943   },
5944   {
5945     Ydirt_eB,                           FALSE,  FALSE,
5946     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
5947   },
5948   {
5949     Ydirt_sB,                           FALSE,  FALSE,
5950     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
5951   },
5952   {
5953     Ydirt_wB,                           FALSE,  FALSE,
5954     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
5955   },
5956   {
5957     Xacid_ne,                           TRUE,   FALSE,
5958     EL_ACID_POOL_TOPRIGHT,              -1, -1
5959   },
5960   {
5961     Xacid_se,                           TRUE,   FALSE,
5962     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
5963   },
5964   {
5965     Xacid_s,                            TRUE,   FALSE,
5966     EL_ACID_POOL_BOTTOM,                -1, -1
5967   },
5968   {
5969     Xacid_sw,                           TRUE,   FALSE,
5970     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
5971   },
5972   {
5973     Xacid_nw,                           TRUE,   FALSE,
5974     EL_ACID_POOL_TOPLEFT,               -1, -1
5975   },
5976   {
5977     Xacid_1,                            TRUE,   FALSE,
5978     EL_ACID,                            -1, -1
5979   },
5980   {
5981     Xacid_2,                            FALSE,  FALSE,
5982     EL_ACID,                            -1, -1
5983   },
5984   {
5985     Xacid_3,                            FALSE,  FALSE,
5986     EL_ACID,                            -1, -1
5987   },
5988   {
5989     Xacid_4,                            FALSE,  FALSE,
5990     EL_ACID,                            -1, -1
5991   },
5992   {
5993     Xacid_5,                            FALSE,  FALSE,
5994     EL_ACID,                            -1, -1
5995   },
5996   {
5997     Xacid_6,                            FALSE,  FALSE,
5998     EL_ACID,                            -1, -1
5999   },
6000   {
6001     Xacid_7,                            FALSE,  FALSE,
6002     EL_ACID,                            -1, -1
6003   },
6004   {
6005     Xacid_8,                            FALSE,  FALSE,
6006     EL_ACID,                            -1, -1
6007   },
6008   {
6009     Xball_1,                            TRUE,   FALSE,
6010     EL_EMC_MAGIC_BALL,                  -1, -1
6011   },
6012   {
6013     Xball_1B,                           FALSE,  FALSE,
6014     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6015   },
6016   {
6017     Xball_2,                            FALSE,  FALSE,
6018     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6019   },
6020   {
6021     Xball_2B,                           FALSE,  FALSE,
6022     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6023   },
6024   {
6025     Yball_eat,                          FALSE,  FALSE,
6026     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
6027   },
6028   {
6029     Ykey_1_eat,                         FALSE,  FALSE,
6030     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
6031   },
6032   {
6033     Ykey_2_eat,                         FALSE,  FALSE,
6034     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
6035   },
6036   {
6037     Ykey_3_eat,                         FALSE,  FALSE,
6038     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
6039   },
6040   {
6041     Ykey_4_eat,                         FALSE,  FALSE,
6042     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
6043   },
6044   {
6045     Ykey_5_eat,                         FALSE,  FALSE,
6046     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
6047   },
6048   {
6049     Ykey_6_eat,                         FALSE,  FALSE,
6050     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
6051   },
6052   {
6053     Ykey_7_eat,                         FALSE,  FALSE,
6054     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
6055   },
6056   {
6057     Ykey_8_eat,                         FALSE,  FALSE,
6058     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
6059   },
6060   {
6061     Ylenses_eat,                        FALSE,  FALSE,
6062     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
6063   },
6064   {
6065     Ymagnify_eat,                       FALSE,  FALSE,
6066     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
6067   },
6068   {
6069     Ygrass_eat,                         FALSE,  FALSE,
6070     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
6071   },
6072   {
6073     Ydirt_eat,                          FALSE,  FALSE,
6074     EL_SAND,                            ACTION_SNAPPING, -1
6075   },
6076   {
6077     Xgrow_ns,                           TRUE,   FALSE,
6078     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
6079   },
6080   {
6081     Ygrow_ns_eat,                       FALSE,  FALSE,
6082     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
6083   },
6084   {
6085     Xgrow_ew,                           TRUE,   FALSE,
6086     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
6087   },
6088   {
6089     Ygrow_ew_eat,                       FALSE,  FALSE,
6090     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
6091   },
6092   {
6093     Xwonderwall,                        TRUE,   FALSE,
6094     EL_MAGIC_WALL,                      -1, -1
6095   },
6096   {
6097     XwonderwallB,                       FALSE,  FALSE,
6098     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
6099   },
6100   {
6101     Xamoeba_1,                          TRUE,   FALSE,
6102     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6103   },
6104   {
6105     Xamoeba_2,                          FALSE,  FALSE,
6106     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6107   },
6108   {
6109     Xamoeba_3,                          FALSE,  FALSE,
6110     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6111   },
6112   {
6113     Xamoeba_4,                          FALSE,  FALSE,
6114     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6115   },
6116   {
6117     Xamoeba_5,                          TRUE,   FALSE,
6118     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6119   },
6120   {
6121     Xamoeba_6,                          FALSE,  FALSE,
6122     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6123   },
6124   {
6125     Xamoeba_7,                          FALSE,  FALSE,
6126     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6127   },
6128   {
6129     Xamoeba_8,                          FALSE,  FALSE,
6130     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6131   },
6132   {
6133     Xdoor_1,                            TRUE,   FALSE,
6134     EL_EM_GATE_1,                       -1, -1
6135   },
6136   {
6137     Xdoor_2,                            TRUE,   FALSE,
6138     EL_EM_GATE_2,                       -1, -1
6139   },
6140   {
6141     Xdoor_3,                            TRUE,   FALSE,
6142     EL_EM_GATE_3,                       -1, -1
6143   },
6144   {
6145     Xdoor_4,                            TRUE,   FALSE,
6146     EL_EM_GATE_4,                       -1, -1
6147   },
6148   {
6149     Xdoor_5,                            TRUE,   FALSE,
6150     EL_EMC_GATE_5,                      -1, -1
6151   },
6152   {
6153     Xdoor_6,                            TRUE,   FALSE,
6154     EL_EMC_GATE_6,                      -1, -1
6155   },
6156   {
6157     Xdoor_7,                            TRUE,   FALSE,
6158     EL_EMC_GATE_7,                      -1, -1
6159   },
6160   {
6161     Xdoor_8,                            TRUE,   FALSE,
6162     EL_EMC_GATE_8,                      -1, -1
6163   },
6164   {
6165     Xkey_1,                             TRUE,   FALSE,
6166     EL_EM_KEY_1,                        -1, -1
6167   },
6168   {
6169     Xkey_2,                             TRUE,   FALSE,
6170     EL_EM_KEY_2,                        -1, -1
6171   },
6172   {
6173     Xkey_3,                             TRUE,   FALSE,
6174     EL_EM_KEY_3,                        -1, -1
6175   },
6176   {
6177     Xkey_4,                             TRUE,   FALSE,
6178     EL_EM_KEY_4,                        -1, -1
6179   },
6180   {
6181     Xkey_5,                             TRUE,   FALSE,
6182     EL_EMC_KEY_5,                       -1, -1
6183   },
6184   {
6185     Xkey_6,                             TRUE,   FALSE,
6186     EL_EMC_KEY_6,                       -1, -1
6187   },
6188   {
6189     Xkey_7,                             TRUE,   FALSE,
6190     EL_EMC_KEY_7,                       -1, -1
6191   },
6192   {
6193     Xkey_8,                             TRUE,   FALSE,
6194     EL_EMC_KEY_8,                       -1, -1
6195   },
6196   {
6197     Xwind_n,                            TRUE,   FALSE,
6198     EL_BALLOON_SWITCH_UP,               -1, -1
6199   },
6200   {
6201     Xwind_e,                            TRUE,   FALSE,
6202     EL_BALLOON_SWITCH_RIGHT,            -1, -1
6203   },
6204   {
6205     Xwind_s,                            TRUE,   FALSE,
6206     EL_BALLOON_SWITCH_DOWN,             -1, -1
6207   },
6208   {
6209     Xwind_w,                            TRUE,   FALSE,
6210     EL_BALLOON_SWITCH_LEFT,             -1, -1
6211   },
6212   {
6213     Xwind_nesw,                         TRUE,   FALSE,
6214     EL_BALLOON_SWITCH_ANY,              -1, -1
6215   },
6216   {
6217     Xwind_stop,                         TRUE,   FALSE,
6218     EL_BALLOON_SWITCH_NONE,             -1, -1
6219   },
6220   {
6221     Xexit,                              TRUE,   FALSE,
6222     EL_EM_EXIT_CLOSED,                  -1, -1
6223   },
6224   {
6225     Xexit_1,                            TRUE,   FALSE,
6226     EL_EM_EXIT_OPEN,                    -1, -1
6227   },
6228   {
6229     Xexit_2,                            FALSE,  FALSE,
6230     EL_EM_EXIT_OPEN,                    -1, -1
6231   },
6232   {
6233     Xexit_3,                            FALSE,  FALSE,
6234     EL_EM_EXIT_OPEN,                    -1, -1
6235   },
6236   {
6237     Xdynamite,                          TRUE,   FALSE,
6238     EL_EM_DYNAMITE,                     -1, -1
6239   },
6240   {
6241     Ydynamite_eat,                      FALSE,  FALSE,
6242     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6243   },
6244   {
6245     Xdynamite_1,                        TRUE,   FALSE,
6246     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6247   },
6248   {
6249     Xdynamite_2,                        FALSE,  FALSE,
6250     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6251   },
6252   {
6253     Xdynamite_3,                        FALSE,  FALSE,
6254     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6255   },
6256   {
6257     Xdynamite_4,                        FALSE,  FALSE,
6258     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6259   },
6260   {
6261     Xbumper,                            TRUE,   FALSE,
6262     EL_EMC_SPRING_BUMPER,               -1, -1
6263   },
6264   {
6265     XbumperB,                           FALSE,  FALSE,
6266     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
6267   },
6268   {
6269     Xwheel,                             TRUE,   FALSE,
6270     EL_ROBOT_WHEEL,                     -1, -1
6271   },
6272   {
6273     XwheelB,                            FALSE,  FALSE,
6274     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
6275   },
6276   {
6277     Xswitch,                            TRUE,   FALSE,
6278     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
6279   },
6280   {
6281     XswitchB,                           FALSE,  FALSE,
6282     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
6283   },
6284   {
6285     Xsand,                              TRUE,   FALSE,
6286     EL_QUICKSAND_EMPTY,                 -1, -1
6287   },
6288   {
6289     Xsand_stone,                        TRUE,   FALSE,
6290     EL_QUICKSAND_FULL,                  -1, -1
6291   },
6292   {
6293     Xsand_stonein_1,                    FALSE,  TRUE,
6294     EL_ROCK,                            ACTION_FILLING, -1
6295   },
6296   {
6297     Xsand_stonein_2,                    FALSE,  TRUE,
6298     EL_ROCK,                            ACTION_FILLING, -1
6299   },
6300   {
6301     Xsand_stonein_3,                    FALSE,  TRUE,
6302     EL_ROCK,                            ACTION_FILLING, -1
6303   },
6304   {
6305     Xsand_stonein_4,                    FALSE,  TRUE,
6306     EL_ROCK,                            ACTION_FILLING, -1
6307   },
6308   {
6309     Xsand_stonesand_1,                  FALSE,  FALSE,
6310     EL_QUICKSAND_EMPTYING,              -1, -1
6311   },
6312   {
6313     Xsand_stonesand_2,                  FALSE,  FALSE,
6314     EL_QUICKSAND_EMPTYING,              -1, -1
6315   },
6316   {
6317     Xsand_stonesand_3,                  FALSE,  FALSE,
6318     EL_QUICKSAND_EMPTYING,              -1, -1
6319   },
6320   {
6321     Xsand_stonesand_4,                  FALSE,  FALSE,
6322     EL_QUICKSAND_EMPTYING,              -1, -1
6323   },
6324   {
6325     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
6326     EL_QUICKSAND_EMPTYING,              -1, -1
6327   },
6328   {
6329     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
6330     EL_QUICKSAND_EMPTYING,              -1, -1
6331   },
6332   {
6333     Xsand_stoneout_1,                   FALSE,  FALSE,
6334     EL_ROCK,                            ACTION_EMPTYING, -1
6335   },
6336   {
6337     Xsand_stoneout_2,                   FALSE,  FALSE,
6338     EL_ROCK,                            ACTION_EMPTYING, -1
6339   },
6340   {
6341     Xsand_sandstone_1,                  FALSE,  FALSE,
6342     EL_QUICKSAND_FILLING,               -1, -1
6343   },
6344   {
6345     Xsand_sandstone_2,                  FALSE,  FALSE,
6346     EL_QUICKSAND_FILLING,               -1, -1
6347   },
6348   {
6349     Xsand_sandstone_3,                  FALSE,  FALSE,
6350     EL_QUICKSAND_FILLING,               -1, -1
6351   },
6352   {
6353     Xsand_sandstone_4,                  FALSE,  FALSE,
6354     EL_QUICKSAND_FILLING,               -1, -1
6355   },
6356   {
6357     Xplant,                             TRUE,   FALSE,
6358     EL_EMC_PLANT,                       -1, -1
6359   },
6360   {
6361     Yplant,                             FALSE,  FALSE,
6362     EL_EMC_PLANT,                       -1, -1
6363   },
6364   {
6365     Xlenses,                            TRUE,   FALSE,
6366     EL_EMC_LENSES,                      -1, -1
6367   },
6368   {
6369     Xmagnify,                           TRUE,   FALSE,
6370     EL_EMC_MAGNIFIER,                   -1, -1
6371   },
6372   {
6373     Xdripper,                           TRUE,   FALSE,
6374     EL_EMC_DRIPPER,                     -1, -1
6375   },
6376   {
6377     XdripperB,                          FALSE,  FALSE,
6378     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
6379   },
6380   {
6381     Xfake_blank,                        TRUE,   FALSE,
6382     EL_INVISIBLE_WALL,                  -1, -1
6383   },
6384   {
6385     Xfake_blankB,                       FALSE,  FALSE,
6386     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
6387   },
6388   {
6389     Xfake_grass,                        TRUE,   FALSE,
6390     EL_EMC_FAKE_GRASS,                  -1, -1
6391   },
6392   {
6393     Xfake_grassB,                       FALSE,  FALSE,
6394     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
6395   },
6396   {
6397     Xfake_door_1,                       TRUE,   FALSE,
6398     EL_EM_GATE_1_GRAY,                  -1, -1
6399   },
6400   {
6401     Xfake_door_2,                       TRUE,   FALSE,
6402     EL_EM_GATE_2_GRAY,                  -1, -1
6403   },
6404   {
6405     Xfake_door_3,                       TRUE,   FALSE,
6406     EL_EM_GATE_3_GRAY,                  -1, -1
6407   },
6408   {
6409     Xfake_door_4,                       TRUE,   FALSE,
6410     EL_EM_GATE_4_GRAY,                  -1, -1
6411   },
6412   {
6413     Xfake_door_5,                       TRUE,   FALSE,
6414     EL_EMC_GATE_5_GRAY,                 -1, -1
6415   },
6416   {
6417     Xfake_door_6,                       TRUE,   FALSE,
6418     EL_EMC_GATE_6_GRAY,                 -1, -1
6419   },
6420   {
6421     Xfake_door_7,                       TRUE,   FALSE,
6422     EL_EMC_GATE_7_GRAY,                 -1, -1
6423   },
6424   {
6425     Xfake_door_8,                       TRUE,   FALSE,
6426     EL_EMC_GATE_8_GRAY,                 -1, -1
6427   },
6428   {
6429     Xfake_acid_1,                       TRUE,   FALSE,
6430     EL_EMC_FAKE_ACID,                   -1, -1
6431   },
6432   {
6433     Xfake_acid_2,                       FALSE,  FALSE,
6434     EL_EMC_FAKE_ACID,                   -1, -1
6435   },
6436   {
6437     Xfake_acid_3,                       FALSE,  FALSE,
6438     EL_EMC_FAKE_ACID,                   -1, -1
6439   },
6440   {
6441     Xfake_acid_4,                       FALSE,  FALSE,
6442     EL_EMC_FAKE_ACID,                   -1, -1
6443   },
6444   {
6445     Xfake_acid_5,                       FALSE,  FALSE,
6446     EL_EMC_FAKE_ACID,                   -1, -1
6447   },
6448   {
6449     Xfake_acid_6,                       FALSE,  FALSE,
6450     EL_EMC_FAKE_ACID,                   -1, -1
6451   },
6452   {
6453     Xfake_acid_7,                       FALSE,  FALSE,
6454     EL_EMC_FAKE_ACID,                   -1, -1
6455   },
6456   {
6457     Xfake_acid_8,                       FALSE,  FALSE,
6458     EL_EMC_FAKE_ACID,                   -1, -1
6459   },
6460   {
6461     Xsteel_1,                           TRUE,   FALSE,
6462     EL_STEELWALL,                       -1, -1
6463   },
6464   {
6465     Xsteel_2,                           TRUE,   FALSE,
6466     EL_EMC_STEELWALL_2,                 -1, -1
6467   },
6468   {
6469     Xsteel_3,                           TRUE,   FALSE,
6470     EL_EMC_STEELWALL_3,                 -1, -1
6471   },
6472   {
6473     Xsteel_4,                           TRUE,   FALSE,
6474     EL_EMC_STEELWALL_4,                 -1, -1
6475   },
6476   {
6477     Xwall_1,                            TRUE,   FALSE,
6478     EL_WALL,                            -1, -1
6479   },
6480   {
6481     Xwall_2,                            TRUE,   FALSE,
6482     EL_EMC_WALL_14,                     -1, -1
6483   },
6484   {
6485     Xwall_3,                            TRUE,   FALSE,
6486     EL_EMC_WALL_15,                     -1, -1
6487   },
6488   {
6489     Xwall_4,                            TRUE,   FALSE,
6490     EL_EMC_WALL_16,                     -1, -1
6491   },
6492   {
6493     Xround_wall_1,                      TRUE,   FALSE,
6494     EL_WALL_SLIPPERY,                   -1, -1
6495   },
6496   {
6497     Xround_wall_2,                      TRUE,   FALSE,
6498     EL_EMC_WALL_SLIPPERY_2,             -1, -1
6499   },
6500   {
6501     Xround_wall_3,                      TRUE,   FALSE,
6502     EL_EMC_WALL_SLIPPERY_3,             -1, -1
6503   },
6504   {
6505     Xround_wall_4,                      TRUE,   FALSE,
6506     EL_EMC_WALL_SLIPPERY_4,             -1, -1
6507   },
6508   {
6509     Xdecor_1,                           TRUE,   FALSE,
6510     EL_EMC_WALL_8,                      -1, -1
6511   },
6512   {
6513     Xdecor_2,                           TRUE,   FALSE,
6514     EL_EMC_WALL_6,                      -1, -1
6515   },
6516   {
6517     Xdecor_3,                           TRUE,   FALSE,
6518     EL_EMC_WALL_4,                      -1, -1
6519   },
6520   {
6521     Xdecor_4,                           TRUE,   FALSE,
6522     EL_EMC_WALL_7,                      -1, -1
6523   },
6524   {
6525     Xdecor_5,                           TRUE,   FALSE,
6526     EL_EMC_WALL_5,                      -1, -1
6527   },
6528   {
6529     Xdecor_6,                           TRUE,   FALSE,
6530     EL_EMC_WALL_9,                      -1, -1
6531   },
6532   {
6533     Xdecor_7,                           TRUE,   FALSE,
6534     EL_EMC_WALL_10,                     -1, -1
6535   },
6536   {
6537     Xdecor_8,                           TRUE,   FALSE,
6538     EL_EMC_WALL_1,                      -1, -1
6539   },
6540   {
6541     Xdecor_9,                           TRUE,   FALSE,
6542     EL_EMC_WALL_2,                      -1, -1
6543   },
6544   {
6545     Xdecor_10,                          TRUE,   FALSE,
6546     EL_EMC_WALL_3,                      -1, -1
6547   },
6548   {
6549     Xdecor_11,                          TRUE,   FALSE,
6550     EL_EMC_WALL_11,                     -1, -1
6551   },
6552   {
6553     Xdecor_12,                          TRUE,   FALSE,
6554     EL_EMC_WALL_12,                     -1, -1
6555   },
6556   {
6557     Xalpha_0,                           TRUE,   FALSE,
6558     EL_CHAR('0'),                       -1, -1
6559   },
6560   {
6561     Xalpha_1,                           TRUE,   FALSE,
6562     EL_CHAR('1'),                       -1, -1
6563   },
6564   {
6565     Xalpha_2,                           TRUE,   FALSE,
6566     EL_CHAR('2'),                       -1, -1
6567   },
6568   {
6569     Xalpha_3,                           TRUE,   FALSE,
6570     EL_CHAR('3'),                       -1, -1
6571   },
6572   {
6573     Xalpha_4,                           TRUE,   FALSE,
6574     EL_CHAR('4'),                       -1, -1
6575   },
6576   {
6577     Xalpha_5,                           TRUE,   FALSE,
6578     EL_CHAR('5'),                       -1, -1
6579   },
6580   {
6581     Xalpha_6,                           TRUE,   FALSE,
6582     EL_CHAR('6'),                       -1, -1
6583   },
6584   {
6585     Xalpha_7,                           TRUE,   FALSE,
6586     EL_CHAR('7'),                       -1, -1
6587   },
6588   {
6589     Xalpha_8,                           TRUE,   FALSE,
6590     EL_CHAR('8'),                       -1, -1
6591   },
6592   {
6593     Xalpha_9,                           TRUE,   FALSE,
6594     EL_CHAR('9'),                       -1, -1
6595   },
6596   {
6597     Xalpha_excla,                       TRUE,   FALSE,
6598     EL_CHAR('!'),                       -1, -1
6599   },
6600   {
6601     Xalpha_quote,                       TRUE,   FALSE,
6602     EL_CHAR('"'),                       -1, -1
6603   },
6604   {
6605     Xalpha_comma,                       TRUE,   FALSE,
6606     EL_CHAR(','),                       -1, -1
6607   },
6608   {
6609     Xalpha_minus,                       TRUE,   FALSE,
6610     EL_CHAR('-'),                       -1, -1
6611   },
6612   {
6613     Xalpha_perio,                       TRUE,   FALSE,
6614     EL_CHAR('.'),                       -1, -1
6615   },
6616   {
6617     Xalpha_colon,                       TRUE,   FALSE,
6618     EL_CHAR(':'),                       -1, -1
6619   },
6620   {
6621     Xalpha_quest,                       TRUE,   FALSE,
6622     EL_CHAR('?'),                       -1, -1
6623   },
6624   {
6625     Xalpha_a,                           TRUE,   FALSE,
6626     EL_CHAR('A'),                       -1, -1
6627   },
6628   {
6629     Xalpha_b,                           TRUE,   FALSE,
6630     EL_CHAR('B'),                       -1, -1
6631   },
6632   {
6633     Xalpha_c,                           TRUE,   FALSE,
6634     EL_CHAR('C'),                       -1, -1
6635   },
6636   {
6637     Xalpha_d,                           TRUE,   FALSE,
6638     EL_CHAR('D'),                       -1, -1
6639   },
6640   {
6641     Xalpha_e,                           TRUE,   FALSE,
6642     EL_CHAR('E'),                       -1, -1
6643   },
6644   {
6645     Xalpha_f,                           TRUE,   FALSE,
6646     EL_CHAR('F'),                       -1, -1
6647   },
6648   {
6649     Xalpha_g,                           TRUE,   FALSE,
6650     EL_CHAR('G'),                       -1, -1
6651   },
6652   {
6653     Xalpha_h,                           TRUE,   FALSE,
6654     EL_CHAR('H'),                       -1, -1
6655   },
6656   {
6657     Xalpha_i,                           TRUE,   FALSE,
6658     EL_CHAR('I'),                       -1, -1
6659   },
6660   {
6661     Xalpha_j,                           TRUE,   FALSE,
6662     EL_CHAR('J'),                       -1, -1
6663   },
6664   {
6665     Xalpha_k,                           TRUE,   FALSE,
6666     EL_CHAR('K'),                       -1, -1
6667   },
6668   {
6669     Xalpha_l,                           TRUE,   FALSE,
6670     EL_CHAR('L'),                       -1, -1
6671   },
6672   {
6673     Xalpha_m,                           TRUE,   FALSE,
6674     EL_CHAR('M'),                       -1, -1
6675   },
6676   {
6677     Xalpha_n,                           TRUE,   FALSE,
6678     EL_CHAR('N'),                       -1, -1
6679   },
6680   {
6681     Xalpha_o,                           TRUE,   FALSE,
6682     EL_CHAR('O'),                       -1, -1
6683   },
6684   {
6685     Xalpha_p,                           TRUE,   FALSE,
6686     EL_CHAR('P'),                       -1, -1
6687   },
6688   {
6689     Xalpha_q,                           TRUE,   FALSE,
6690     EL_CHAR('Q'),                       -1, -1
6691   },
6692   {
6693     Xalpha_r,                           TRUE,   FALSE,
6694     EL_CHAR('R'),                       -1, -1
6695   },
6696   {
6697     Xalpha_s,                           TRUE,   FALSE,
6698     EL_CHAR('S'),                       -1, -1
6699   },
6700   {
6701     Xalpha_t,                           TRUE,   FALSE,
6702     EL_CHAR('T'),                       -1, -1
6703   },
6704   {
6705     Xalpha_u,                           TRUE,   FALSE,
6706     EL_CHAR('U'),                       -1, -1
6707   },
6708   {
6709     Xalpha_v,                           TRUE,   FALSE,
6710     EL_CHAR('V'),                       -1, -1
6711   },
6712   {
6713     Xalpha_w,                           TRUE,   FALSE,
6714     EL_CHAR('W'),                       -1, -1
6715   },
6716   {
6717     Xalpha_x,                           TRUE,   FALSE,
6718     EL_CHAR('X'),                       -1, -1
6719   },
6720   {
6721     Xalpha_y,                           TRUE,   FALSE,
6722     EL_CHAR('Y'),                       -1, -1
6723   },
6724   {
6725     Xalpha_z,                           TRUE,   FALSE,
6726     EL_CHAR('Z'),                       -1, -1
6727   },
6728   {
6729     Xalpha_arrow_e,                     TRUE,   FALSE,
6730     EL_CHAR('>'),                       -1, -1
6731   },
6732   {
6733     Xalpha_arrow_w,                     TRUE,   FALSE,
6734     EL_CHAR('<'),                       -1, -1
6735   },
6736   {
6737     Xalpha_copyr,                       TRUE,   FALSE,
6738     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
6739   },
6740
6741   {
6742     Xboom_bug,                          FALSE,  FALSE,
6743     EL_BUG,                             ACTION_EXPLODING, -1
6744   },
6745   {
6746     Xboom_bomb,                         FALSE,  FALSE,
6747     EL_BOMB,                            ACTION_EXPLODING, -1
6748   },
6749   {
6750     Xboom_android,                      FALSE,  FALSE,
6751     EL_EMC_ANDROID,                     ACTION_OTHER, -1
6752   },
6753   {
6754     Xboom_1,                            FALSE,  FALSE,
6755     EL_DEFAULT,                         ACTION_EXPLODING, -1
6756   },
6757   {
6758     Xboom_2,                            FALSE,  FALSE,
6759     EL_DEFAULT,                         ACTION_EXPLODING, -1
6760   },
6761   {
6762     Znormal,                            FALSE,  FALSE,
6763     EL_EMPTY,                           -1, -1
6764   },
6765   {
6766     Zdynamite,                          FALSE,  FALSE,
6767     EL_EMPTY,                           -1, -1
6768   },
6769   {
6770     Zplayer,                            FALSE,  FALSE,
6771     EL_EMPTY,                           -1, -1
6772   },
6773   {
6774     ZBORDER,                            FALSE,  FALSE,
6775     EL_EMPTY,                           -1, -1
6776   },
6777
6778   {
6779     -1,                                 FALSE,  FALSE,
6780     -1,                                 -1, -1
6781   }
6782 };
6783
6784 static struct Mapping_EM_to_RND_player
6785 {
6786   int action_em;
6787   int player_nr;
6788
6789   int element_rnd;
6790   int action;
6791   int direction;
6792 }
6793 em_player_mapping_list[] =
6794 {
6795   {
6796     SPR_walk + 0,                       0,
6797     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
6798   },
6799   {
6800     SPR_walk + 1,                       0,
6801     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
6802   },
6803   {
6804     SPR_walk + 2,                       0,
6805     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
6806   },
6807   {
6808     SPR_walk + 3,                       0,
6809     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
6810   },
6811   {
6812     SPR_push + 0,                       0,
6813     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
6814   },
6815   {
6816     SPR_push + 1,                       0,
6817     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
6818   },
6819   {
6820     SPR_push + 2,                       0,
6821     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
6822   },
6823   {
6824     SPR_push + 3,                       0,
6825     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
6826   },
6827   {
6828     SPR_spray + 0,                      0,
6829     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
6830   },
6831   {
6832     SPR_spray + 1,                      0,
6833     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6834   },
6835   {
6836     SPR_spray + 2,                      0,
6837     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
6838   },
6839   {
6840     SPR_spray + 3,                      0,
6841     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
6842   },
6843   {
6844     SPR_walk + 0,                       1,
6845     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
6846   },
6847   {
6848     SPR_walk + 1,                       1,
6849     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
6850   },
6851   {
6852     SPR_walk + 2,                       1,
6853     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
6854   },
6855   {
6856     SPR_walk + 3,                       1,
6857     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
6858   },
6859   {
6860     SPR_push + 0,                       1,
6861     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
6862   },
6863   {
6864     SPR_push + 1,                       1,
6865     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
6866   },
6867   {
6868     SPR_push + 2,                       1,
6869     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
6870   },
6871   {
6872     SPR_push + 3,                       1,
6873     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
6874   },
6875   {
6876     SPR_spray + 0,                      1,
6877     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
6878   },
6879   {
6880     SPR_spray + 1,                      1,
6881     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6882   },
6883   {
6884     SPR_spray + 2,                      1,
6885     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
6886   },
6887   {
6888     SPR_spray + 3,                      1,
6889     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
6890   },
6891   {
6892     SPR_still,                          0,
6893     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
6894   },
6895   {
6896     SPR_still,                          1,
6897     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
6898   },
6899   {
6900     SPR_walk + 0,                       2,
6901     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
6902   },
6903   {
6904     SPR_walk + 1,                       2,
6905     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
6906   },
6907   {
6908     SPR_walk + 2,                       2,
6909     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
6910   },
6911   {
6912     SPR_walk + 3,                       2,
6913     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
6914   },
6915   {
6916     SPR_push + 0,                       2,
6917     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
6918   },
6919   {
6920     SPR_push + 1,                       2,
6921     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
6922   },
6923   {
6924     SPR_push + 2,                       2,
6925     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
6926   },
6927   {
6928     SPR_push + 3,                       2,
6929     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
6930   },
6931   {
6932     SPR_spray + 0,                      2,
6933     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
6934   },
6935   {
6936     SPR_spray + 1,                      2,
6937     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6938   },
6939   {
6940     SPR_spray + 2,                      2,
6941     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
6942   },
6943   {
6944     SPR_spray + 3,                      2,
6945     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
6946   },
6947   {
6948     SPR_walk + 0,                       3,
6949     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
6950   },
6951   {
6952     SPR_walk + 1,                       3,
6953     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
6954   },
6955   {
6956     SPR_walk + 2,                       3,
6957     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
6958   },
6959   {
6960     SPR_walk + 3,                       3,
6961     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
6962   },
6963   {
6964     SPR_push + 0,                       3,
6965     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
6966   },
6967   {
6968     SPR_push + 1,                       3,
6969     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
6970   },
6971   {
6972     SPR_push + 2,                       3,
6973     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
6974   },
6975   {
6976     SPR_push + 3,                       3,
6977     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
6978   },
6979   {
6980     SPR_spray + 0,                      3,
6981     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
6982   },
6983   {
6984     SPR_spray + 1,                      3,
6985     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6986   },
6987   {
6988     SPR_spray + 2,                      3,
6989     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
6990   },
6991   {
6992     SPR_spray + 3,                      3,
6993     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
6994   },
6995   {
6996     SPR_still,                          2,
6997     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
6998   },
6999   {
7000     SPR_still,                          3,
7001     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7002   },
7003
7004   {
7005     -1,                                 -1,
7006     -1,                                 -1, -1
7007   }
7008 };
7009
7010 int map_element_RND_to_EM(int element_rnd)
7011 {
7012   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7013   static boolean mapping_initialized = FALSE;
7014
7015   if (!mapping_initialized)
7016   {
7017     int i;
7018
7019     /* return "Xalpha_quest" for all undefined elements in mapping array */
7020     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7021       mapping_RND_to_EM[i] = Xalpha_quest;
7022
7023     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7024       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7025         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7026           em_object_mapping_list[i].element_em;
7027
7028     mapping_initialized = TRUE;
7029   }
7030
7031   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7032     return mapping_RND_to_EM[element_rnd];
7033
7034   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7035
7036   return EL_UNKNOWN;
7037 }
7038
7039 int map_element_EM_to_RND(int element_em)
7040 {
7041   static unsigned short mapping_EM_to_RND[TILE_MAX];
7042   static boolean mapping_initialized = FALSE;
7043
7044   if (!mapping_initialized)
7045   {
7046     int i;
7047
7048     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7049     for (i = 0; i < TILE_MAX; i++)
7050       mapping_EM_to_RND[i] = EL_UNKNOWN;
7051
7052     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7053       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7054         em_object_mapping_list[i].element_rnd;
7055
7056     mapping_initialized = TRUE;
7057   }
7058
7059   if (element_em >= 0 && element_em < TILE_MAX)
7060     return mapping_EM_to_RND[element_em];
7061
7062   Error(ERR_WARN, "invalid EM level element %d", element_em);
7063
7064   return EL_UNKNOWN;
7065 }
7066
7067 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7068 {
7069   struct LevelInfo_EM *level_em = level->native_em_level;
7070   struct LEVEL *lev = level_em->lev;
7071   int i, j;
7072
7073   for (i = 0; i < TILE_MAX; i++)
7074     lev->android_array[i] = Xblank;
7075
7076   for (i = 0; i < level->num_android_clone_elements; i++)
7077   {
7078     int element_rnd = level->android_clone_element[i];
7079     int element_em = map_element_RND_to_EM(element_rnd);
7080
7081     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7082       if (em_object_mapping_list[j].element_rnd == element_rnd)
7083         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7084   }
7085 }
7086
7087 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7088 {
7089   struct LevelInfo_EM *level_em = level->native_em_level;
7090   struct LEVEL *lev = level_em->lev;
7091   int i, j;
7092
7093   level->num_android_clone_elements = 0;
7094
7095   for (i = 0; i < TILE_MAX; i++)
7096   {
7097     int element_em = lev->android_array[i];
7098     int element_rnd;
7099     boolean element_found = FALSE;
7100
7101     if (element_em == Xblank)
7102       continue;
7103
7104     element_rnd = map_element_EM_to_RND(element_em);
7105
7106     for (j = 0; j < level->num_android_clone_elements; j++)
7107       if (level->android_clone_element[j] == element_rnd)
7108         element_found = TRUE;
7109
7110     if (!element_found)
7111     {
7112       level->android_clone_element[level->num_android_clone_elements++] =
7113         element_rnd;
7114
7115       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7116         break;
7117     }
7118   }
7119
7120   if (level->num_android_clone_elements == 0)
7121   {
7122     level->num_android_clone_elements = 1;
7123     level->android_clone_element[0] = EL_EMPTY;
7124   }
7125 }
7126
7127 int map_direction_RND_to_EM(int direction)
7128 {
7129   return (direction == MV_UP    ? 0 :
7130           direction == MV_RIGHT ? 1 :
7131           direction == MV_DOWN  ? 2 :
7132           direction == MV_LEFT  ? 3 :
7133           -1);
7134 }
7135
7136 int map_direction_EM_to_RND(int direction)
7137 {
7138   return (direction == 0 ? MV_UP    :
7139           direction == 1 ? MV_RIGHT :
7140           direction == 2 ? MV_DOWN  :
7141           direction == 3 ? MV_LEFT  :
7142           MV_NONE);
7143 }
7144
7145 int map_element_RND_to_SP(int element_rnd)
7146 {
7147   int element_sp = 0x20;        /* map unknown elements to yellow "hardware" */
7148
7149   if (element_rnd >= EL_SP_START &&
7150       element_rnd <= EL_SP_END)
7151     element_sp = element_rnd - EL_SP_START;
7152   else if (element_rnd == EL_EMPTY_SPACE)
7153     element_sp = 0x00;
7154   else if (element_rnd == EL_INVISIBLE_WALL)
7155     element_sp = 0x28;
7156
7157   return element_sp;
7158 }
7159
7160 int map_element_SP_to_RND(int element_sp)
7161 {
7162   int element_rnd = EL_UNKNOWN;
7163
7164   if (element_sp >= 0x00 &&
7165       element_sp <= 0x27)
7166     element_rnd = EL_SP_START + element_sp;
7167   else if (element_sp == 0x28)
7168     element_rnd = EL_INVISIBLE_WALL;
7169
7170   return element_rnd;
7171 }
7172
7173 int map_action_SP_to_RND(int action_sp)
7174 {
7175   switch (action_sp)
7176   {
7177     case actActive:             return ACTION_ACTIVE;
7178     case actImpact:             return ACTION_IMPACT;
7179     case actExploding:          return ACTION_EXPLODING;
7180     case actDigging:            return ACTION_DIGGING;
7181     case actSnapping:           return ACTION_SNAPPING;
7182     case actCollecting:         return ACTION_COLLECTING;
7183     case actPassing:            return ACTION_PASSING;
7184     case actPushing:            return ACTION_PUSHING;
7185     case actDropping:           return ACTION_DROPPING;
7186
7187     default:                    return ACTION_DEFAULT;
7188   }
7189 }
7190
7191 int get_next_element(int element)
7192 {
7193   switch (element)
7194   {
7195     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
7196     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
7197     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
7198     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
7199     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
7200     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
7201     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
7202     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
7203     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
7204     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
7205     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
7206
7207     default:                            return element;
7208   }
7209 }
7210
7211 int el_act_dir2img(int element, int action, int direction)
7212 {
7213   element = GFX_ELEMENT(element);
7214   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7215
7216   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7217   return element_info[element].direction_graphic[action][direction];
7218 }
7219
7220 static int el_act_dir2crm(int element, int action, int direction)
7221 {
7222   element = GFX_ELEMENT(element);
7223   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7224
7225   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7226   return element_info[element].direction_crumbled[action][direction];
7227 }
7228
7229 int el_act2img(int element, int action)
7230 {
7231   element = GFX_ELEMENT(element);
7232
7233   return element_info[element].graphic[action];
7234 }
7235
7236 int el_act2crm(int element, int action)
7237 {
7238   element = GFX_ELEMENT(element);
7239
7240   return element_info[element].crumbled[action];
7241 }
7242
7243 int el_dir2img(int element, int direction)
7244 {
7245   element = GFX_ELEMENT(element);
7246
7247   return el_act_dir2img(element, ACTION_DEFAULT, direction);
7248 }
7249
7250 int el2baseimg(int element)
7251 {
7252   return element_info[element].graphic[ACTION_DEFAULT];
7253 }
7254
7255 int el2img(int element)
7256 {
7257   element = GFX_ELEMENT(element);
7258
7259   return element_info[element].graphic[ACTION_DEFAULT];
7260 }
7261
7262 int el2edimg(int element)
7263 {
7264   element = GFX_ELEMENT(element);
7265
7266   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7267 }
7268
7269 int el2preimg(int element)
7270 {
7271   element = GFX_ELEMENT(element);
7272
7273   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7274 }
7275
7276 int el2panelimg(int element)
7277 {
7278   element = GFX_ELEMENT(element);
7279
7280   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7281 }
7282
7283 int font2baseimg(int font_nr)
7284 {
7285   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7286 }
7287
7288 int getBeltNrFromBeltElement(int element)
7289 {
7290   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7291           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7292           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7293 }
7294
7295 int getBeltNrFromBeltActiveElement(int element)
7296 {
7297   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7298           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7299           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7300 }
7301
7302 int getBeltNrFromBeltSwitchElement(int element)
7303 {
7304   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7305           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7306           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7307 }
7308
7309 int getBeltDirNrFromBeltElement(int element)
7310 {
7311   static int belt_base_element[4] =
7312   {
7313     EL_CONVEYOR_BELT_1_LEFT,
7314     EL_CONVEYOR_BELT_2_LEFT,
7315     EL_CONVEYOR_BELT_3_LEFT,
7316     EL_CONVEYOR_BELT_4_LEFT
7317   };
7318
7319   int belt_nr = getBeltNrFromBeltElement(element);
7320   int belt_dir_nr = element - belt_base_element[belt_nr];
7321
7322   return (belt_dir_nr % 3);
7323 }
7324
7325 int getBeltDirNrFromBeltSwitchElement(int element)
7326 {
7327   static int belt_base_element[4] =
7328   {
7329     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7330     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7331     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7332     EL_CONVEYOR_BELT_4_SWITCH_LEFT
7333   };
7334
7335   int belt_nr = getBeltNrFromBeltSwitchElement(element);
7336   int belt_dir_nr = element - belt_base_element[belt_nr];
7337
7338   return (belt_dir_nr % 3);
7339 }
7340
7341 int getBeltDirFromBeltElement(int element)
7342 {
7343   static int belt_move_dir[3] =
7344   {
7345     MV_LEFT,
7346     MV_NONE,
7347     MV_RIGHT
7348   };
7349
7350   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7351
7352   return belt_move_dir[belt_dir_nr];
7353 }
7354
7355 int getBeltDirFromBeltSwitchElement(int element)
7356 {
7357   static int belt_move_dir[3] =
7358   {
7359     MV_LEFT,
7360     MV_NONE,
7361     MV_RIGHT
7362   };
7363
7364   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7365
7366   return belt_move_dir[belt_dir_nr];
7367 }
7368
7369 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7370 {
7371   static int belt_base_element[4] =
7372   {
7373     EL_CONVEYOR_BELT_1_LEFT,
7374     EL_CONVEYOR_BELT_2_LEFT,
7375     EL_CONVEYOR_BELT_3_LEFT,
7376     EL_CONVEYOR_BELT_4_LEFT
7377   };
7378
7379   return belt_base_element[belt_nr] + belt_dir_nr;
7380 }
7381
7382 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7383 {
7384   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7385
7386   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7387 }
7388
7389 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7390 {
7391   static int belt_base_element[4] =
7392   {
7393     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7394     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7395     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7396     EL_CONVEYOR_BELT_4_SWITCH_LEFT
7397   };
7398
7399   return belt_base_element[belt_nr] + belt_dir_nr;
7400 }
7401
7402 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7403 {
7404   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7405
7406   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7407 }
7408
7409 boolean getTeamMode_EM()
7410 {
7411   return game.team_mode;
7412 }
7413
7414 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7415 {
7416   int game_frame_delay_value;
7417
7418   game_frame_delay_value =
7419     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7420      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7421      GameFrameDelay);
7422
7423   if (tape.playing && tape.warp_forward && !tape.pausing)
7424     game_frame_delay_value = 0;
7425
7426   return game_frame_delay_value;
7427 }
7428
7429 unsigned int InitRND(int seed)
7430 {
7431   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7432     return InitEngineRandom_EM(seed);
7433   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7434     return InitEngineRandom_SP(seed);
7435   else
7436     return InitEngineRandom_RND(seed);
7437 }
7438
7439 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7440 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7441
7442 inline static int get_effective_element_EM(int tile, int frame_em)
7443 {
7444   int element             = object_mapping[tile].element_rnd;
7445   int action              = object_mapping[tile].action;
7446   boolean is_backside     = object_mapping[tile].is_backside;
7447   boolean action_removing = (action == ACTION_DIGGING ||
7448                              action == ACTION_SNAPPING ||
7449                              action == ACTION_COLLECTING);
7450
7451   if (frame_em < 7)
7452   {
7453     switch (tile)
7454     {
7455       case Yacid_splash_eB:
7456       case Yacid_splash_wB:
7457         return (frame_em > 5 ? EL_EMPTY : element);
7458
7459       default:
7460         return element;
7461     }
7462   }
7463   else  /* frame_em == 7 */
7464   {
7465     switch (tile)
7466     {
7467       case Yacid_splash_eB:
7468       case Yacid_splash_wB:
7469         return EL_EMPTY;
7470
7471       case Yemerald_stone:
7472         return EL_EMERALD;
7473
7474       case Ydiamond_stone:
7475         return EL_ROCK;
7476
7477       case Xdrip_stretch:
7478       case Xdrip_stretchB:
7479       case Ydrip_s1:
7480       case Ydrip_s1B:
7481       case Xball_1B:
7482       case Xball_2:
7483       case Xball_2B:
7484       case Yball_eat:
7485       case Ykey_1_eat:
7486       case Ykey_2_eat:
7487       case Ykey_3_eat:
7488       case Ykey_4_eat:
7489       case Ykey_5_eat:
7490       case Ykey_6_eat:
7491       case Ykey_7_eat:
7492       case Ykey_8_eat:
7493       case Ylenses_eat:
7494       case Ymagnify_eat:
7495       case Ygrass_eat:
7496       case Ydirt_eat:
7497       case Xsand_stonein_1:
7498       case Xsand_stonein_2:
7499       case Xsand_stonein_3:
7500       case Xsand_stonein_4:
7501         return element;
7502
7503       default:
7504         return (is_backside || action_removing ? EL_EMPTY : element);
7505     }
7506   }
7507 }
7508
7509 inline static boolean check_linear_animation_EM(int tile)
7510 {
7511   switch (tile)
7512   {
7513     case Xsand_stonesand_1:
7514     case Xsand_stonesand_quickout_1:
7515     case Xsand_sandstone_1:
7516     case Xsand_stonein_1:
7517     case Xsand_stoneout_1:
7518     case Xboom_1:
7519     case Xdynamite_1:
7520     case Ybug_w_n:
7521     case Ybug_n_e:
7522     case Ybug_e_s:
7523     case Ybug_s_w:
7524     case Ybug_e_n:
7525     case Ybug_s_e:
7526     case Ybug_w_s:
7527     case Ybug_n_w:
7528     case Ytank_w_n:
7529     case Ytank_n_e:
7530     case Ytank_e_s:
7531     case Ytank_s_w:
7532     case Ytank_e_n:
7533     case Ytank_s_e:
7534     case Ytank_w_s:
7535     case Ytank_n_w:
7536     case Yacid_splash_eB:
7537     case Yacid_splash_wB:
7538     case Yemerald_stone:
7539       return TRUE;
7540   }
7541
7542   return FALSE;
7543 }
7544
7545 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7546                                             boolean has_crumbled_graphics,
7547                                             int crumbled, int sync_frame)
7548 {
7549   /* if element can be crumbled, but certain action graphics are just empty
7550      space (like instantly snapping sand to empty space in 1 frame), do not
7551      treat these empty space graphics as crumbled graphics in EMC engine */
7552   if (crumbled == IMG_EMPTY_SPACE)
7553     has_crumbled_graphics = FALSE;
7554
7555   if (has_crumbled_graphics)
7556   {
7557     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7558     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7559                                            g_crumbled->anim_delay,
7560                                            g_crumbled->anim_mode,
7561                                            g_crumbled->anim_start_frame,
7562                                            sync_frame);
7563
7564     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7565                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7566
7567     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7568     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7569
7570     g_em->has_crumbled_graphics = TRUE;
7571   }
7572   else
7573   {
7574     g_em->crumbled_bitmap = NULL;
7575     g_em->crumbled_src_x = 0;
7576     g_em->crumbled_src_y = 0;
7577     g_em->crumbled_border_size = 0;
7578     g_em->crumbled_tile_size = 0;
7579
7580     g_em->has_crumbled_graphics = FALSE;
7581   }
7582 }
7583
7584 void ResetGfxAnimation_EM(int x, int y, int tile)
7585 {
7586   GfxFrame[x][y] = 0;
7587 }
7588
7589 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7590                         int tile, int frame_em, int x, int y)
7591 {
7592   int action = object_mapping[tile].action;
7593   int direction = object_mapping[tile].direction;
7594   int effective_element = get_effective_element_EM(tile, frame_em);
7595   int graphic = (direction == MV_NONE ?
7596                  el_act2img(effective_element, action) :
7597                  el_act_dir2img(effective_element, action, direction));
7598   struct GraphicInfo *g = &graphic_info[graphic];
7599   int sync_frame;
7600   boolean action_removing = (action == ACTION_DIGGING ||
7601                              action == ACTION_SNAPPING ||
7602                              action == ACTION_COLLECTING);
7603   boolean action_moving   = (action == ACTION_FALLING ||
7604                              action == ACTION_MOVING ||
7605                              action == ACTION_PUSHING ||
7606                              action == ACTION_EATING ||
7607                              action == ACTION_FILLING ||
7608                              action == ACTION_EMPTYING);
7609   boolean action_falling  = (action == ACTION_FALLING ||
7610                              action == ACTION_FILLING ||
7611                              action == ACTION_EMPTYING);
7612
7613   /* special case: graphic uses "2nd movement tile" and has defined
7614      7 frames for movement animation (or less) => use default graphic
7615      for last (8th) frame which ends the movement animation */
7616   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7617   {
7618     action = ACTION_DEFAULT;    /* (keep action_* unchanged for now) */
7619     graphic = (direction == MV_NONE ?
7620                el_act2img(effective_element, action) :
7621                el_act_dir2img(effective_element, action, direction));
7622
7623     g = &graphic_info[graphic];
7624   }
7625
7626   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7627   {
7628     GfxFrame[x][y] = 0;
7629   }
7630   else if (action_moving)
7631   {
7632     boolean is_backside = object_mapping[tile].is_backside;
7633
7634     if (is_backside)
7635     {
7636       int direction = object_mapping[tile].direction;
7637       int move_dir = (action_falling ? MV_DOWN : direction);
7638
7639       GfxFrame[x][y]++;
7640
7641 #if 1
7642       /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7643       if (g->double_movement && frame_em == 0)
7644         GfxFrame[x][y] = 0;
7645 #endif
7646
7647       if (move_dir == MV_LEFT)
7648         GfxFrame[x - 1][y] = GfxFrame[x][y];
7649       else if (move_dir == MV_RIGHT)
7650         GfxFrame[x + 1][y] = GfxFrame[x][y];
7651       else if (move_dir == MV_UP)
7652         GfxFrame[x][y - 1] = GfxFrame[x][y];
7653       else if (move_dir == MV_DOWN)
7654         GfxFrame[x][y + 1] = GfxFrame[x][y];
7655     }
7656   }
7657   else
7658   {
7659     GfxFrame[x][y]++;
7660
7661     /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7662     if (tile == Xsand_stonesand_quickout_1 ||
7663         tile == Xsand_stonesand_quickout_2)
7664       GfxFrame[x][y]++;
7665   }
7666
7667   if (graphic_info[graphic].anim_global_sync)
7668     sync_frame = FrameCounter;
7669   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7670     sync_frame = GfxFrame[x][y];
7671   else
7672     sync_frame = 0;     /* playfield border (pseudo steel) */
7673
7674   SetRandomAnimationValue(x, y);
7675
7676   int frame = getAnimationFrame(g->anim_frames,
7677                                 g->anim_delay,
7678                                 g->anim_mode,
7679                                 g->anim_start_frame,
7680                                 sync_frame);
7681
7682   g_em->unique_identifier =
7683     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7684 }
7685
7686 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7687                                   int tile, int frame_em, int x, int y)
7688 {
7689   int action = object_mapping[tile].action;
7690   int direction = object_mapping[tile].direction;
7691   boolean is_backside = object_mapping[tile].is_backside;
7692   int effective_element = get_effective_element_EM(tile, frame_em);
7693   int effective_action = action;
7694   int graphic = (direction == MV_NONE ?
7695                  el_act2img(effective_element, effective_action) :
7696                  el_act_dir2img(effective_element, effective_action,
7697                                 direction));
7698   int crumbled = (direction == MV_NONE ?
7699                   el_act2crm(effective_element, effective_action) :
7700                   el_act_dir2crm(effective_element, effective_action,
7701                                  direction));
7702   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7703   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7704   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7705   struct GraphicInfo *g = &graphic_info[graphic];
7706   int sync_frame;
7707
7708   /* special case: graphic uses "2nd movement tile" and has defined
7709      7 frames for movement animation (or less) => use default graphic
7710      for last (8th) frame which ends the movement animation */
7711   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7712   {
7713     effective_action = ACTION_DEFAULT;
7714     graphic = (direction == MV_NONE ?
7715                el_act2img(effective_element, effective_action) :
7716                el_act_dir2img(effective_element, effective_action,
7717                               direction));
7718     crumbled = (direction == MV_NONE ?
7719                 el_act2crm(effective_element, effective_action) :
7720                 el_act_dir2crm(effective_element, effective_action,
7721                                direction));
7722
7723     g = &graphic_info[graphic];
7724   }
7725
7726   if (graphic_info[graphic].anim_global_sync)
7727     sync_frame = FrameCounter;
7728   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7729     sync_frame = GfxFrame[x][y];
7730   else
7731     sync_frame = 0;     /* playfield border (pseudo steel) */
7732
7733   SetRandomAnimationValue(x, y);
7734
7735   int frame = getAnimationFrame(g->anim_frames,
7736                                 g->anim_delay,
7737                                 g->anim_mode,
7738                                 g->anim_start_frame,
7739                                 sync_frame);
7740
7741   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7742                       g->double_movement && is_backside);
7743
7744   /* (updating the "crumbled" graphic definitions is probably not really needed,
7745      as animations for crumbled graphics can't be longer than one EMC cycle) */
7746   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7747                            sync_frame);
7748 }
7749
7750 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7751                                   int player_nr, int anim, int frame_em)
7752 {
7753   int element   = player_mapping[player_nr][anim].element_rnd;
7754   int action    = player_mapping[player_nr][anim].action;
7755   int direction = player_mapping[player_nr][anim].direction;
7756   int graphic = (direction == MV_NONE ?
7757                  el_act2img(element, action) :
7758                  el_act_dir2img(element, action, direction));
7759   struct GraphicInfo *g = &graphic_info[graphic];
7760   int sync_frame;
7761
7762   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7763
7764   stored_player[player_nr].StepFrame = frame_em;
7765
7766   sync_frame = stored_player[player_nr].Frame;
7767
7768   int frame = getAnimationFrame(g->anim_frames,
7769                                 g->anim_delay,
7770                                 g->anim_mode,
7771                                 g->anim_start_frame,
7772                                 sync_frame);
7773
7774   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7775                       &g_em->src_x, &g_em->src_y, FALSE);
7776 }
7777
7778 void InitGraphicInfo_EM(void)
7779 {
7780   int i, j, p;
7781
7782 #if DEBUG_EM_GFX
7783   int num_em_gfx_errors = 0;
7784
7785   if (graphic_info_em_object[0][0].bitmap == NULL)
7786   {
7787     /* EM graphics not yet initialized in em_open_all() */
7788
7789     return;
7790   }
7791
7792   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7793 #endif
7794
7795   /* always start with reliable default values */
7796   for (i = 0; i < TILE_MAX; i++)
7797   {
7798     object_mapping[i].element_rnd = EL_UNKNOWN;
7799     object_mapping[i].is_backside = FALSE;
7800     object_mapping[i].action = ACTION_DEFAULT;
7801     object_mapping[i].direction = MV_NONE;
7802   }
7803
7804   /* always start with reliable default values */
7805   for (p = 0; p < MAX_PLAYERS; p++)
7806   {
7807     for (i = 0; i < SPR_MAX; i++)
7808     {
7809       player_mapping[p][i].element_rnd = EL_UNKNOWN;
7810       player_mapping[p][i].action = ACTION_DEFAULT;
7811       player_mapping[p][i].direction = MV_NONE;
7812     }
7813   }
7814
7815   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7816   {
7817     int e = em_object_mapping_list[i].element_em;
7818
7819     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7820     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7821
7822     if (em_object_mapping_list[i].action != -1)
7823       object_mapping[e].action = em_object_mapping_list[i].action;
7824
7825     if (em_object_mapping_list[i].direction != -1)
7826       object_mapping[e].direction =
7827         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7828   }
7829
7830   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7831   {
7832     int a = em_player_mapping_list[i].action_em;
7833     int p = em_player_mapping_list[i].player_nr;
7834
7835     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7836
7837     if (em_player_mapping_list[i].action != -1)
7838       player_mapping[p][a].action = em_player_mapping_list[i].action;
7839
7840     if (em_player_mapping_list[i].direction != -1)
7841       player_mapping[p][a].direction =
7842         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7843   }
7844
7845   for (i = 0; i < TILE_MAX; i++)
7846   {
7847     int element = object_mapping[i].element_rnd;
7848     int action = object_mapping[i].action;
7849     int direction = object_mapping[i].direction;
7850     boolean is_backside = object_mapping[i].is_backside;
7851     boolean action_exploding = ((action == ACTION_EXPLODING ||
7852                                  action == ACTION_SMASHED_BY_ROCK ||
7853                                  action == ACTION_SMASHED_BY_SPRING) &&
7854                                 element != EL_DIAMOND);
7855     boolean action_active = (action == ACTION_ACTIVE);
7856     boolean action_other = (action == ACTION_OTHER);
7857
7858     for (j = 0; j < 8; j++)
7859     {
7860       int effective_element = get_effective_element_EM(i, j);
7861       int effective_action = (j < 7 ? action :
7862                               i == Xdrip_stretch ? action :
7863                               i == Xdrip_stretchB ? action :
7864                               i == Ydrip_s1 ? action :
7865                               i == Ydrip_s1B ? action :
7866                               i == Xball_1B ? action :
7867                               i == Xball_2 ? action :
7868                               i == Xball_2B ? action :
7869                               i == Yball_eat ? action :
7870                               i == Ykey_1_eat ? action :
7871                               i == Ykey_2_eat ? action :
7872                               i == Ykey_3_eat ? action :
7873                               i == Ykey_4_eat ? action :
7874                               i == Ykey_5_eat ? action :
7875                               i == Ykey_6_eat ? action :
7876                               i == Ykey_7_eat ? action :
7877                               i == Ykey_8_eat ? action :
7878                               i == Ylenses_eat ? action :
7879                               i == Ymagnify_eat ? action :
7880                               i == Ygrass_eat ? action :
7881                               i == Ydirt_eat ? action :
7882                               i == Xsand_stonein_1 ? action :
7883                               i == Xsand_stonein_2 ? action :
7884                               i == Xsand_stonein_3 ? action :
7885                               i == Xsand_stonein_4 ? action :
7886                               i == Xsand_stoneout_1 ? action :
7887                               i == Xsand_stoneout_2 ? action :
7888                               i == Xboom_android ? ACTION_EXPLODING :
7889                               action_exploding ? ACTION_EXPLODING :
7890                               action_active ? action :
7891                               action_other ? action :
7892                               ACTION_DEFAULT);
7893       int graphic = (el_act_dir2img(effective_element, effective_action,
7894                                     direction));
7895       int crumbled = (el_act_dir2crm(effective_element, effective_action,
7896                                      direction));
7897       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7898       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7899       boolean has_action_graphics = (graphic != base_graphic);
7900       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7901       struct GraphicInfo *g = &graphic_info[graphic];
7902       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7903       Bitmap *src_bitmap;
7904       int src_x, src_y;
7905       /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7906       boolean special_animation = (action != ACTION_DEFAULT &&
7907                                    g->anim_frames == 3 &&
7908                                    g->anim_delay == 2 &&
7909                                    g->anim_mode & ANIM_LINEAR);
7910       int sync_frame = (i == Xdrip_stretch ? 7 :
7911                         i == Xdrip_stretchB ? 7 :
7912                         i == Ydrip_s2 ? j + 8 :
7913                         i == Ydrip_s2B ? j + 8 :
7914                         i == Xacid_1 ? 0 :
7915                         i == Xacid_2 ? 10 :
7916                         i == Xacid_3 ? 20 :
7917                         i == Xacid_4 ? 30 :
7918                         i == Xacid_5 ? 40 :
7919                         i == Xacid_6 ? 50 :
7920                         i == Xacid_7 ? 60 :
7921                         i == Xacid_8 ? 70 :
7922                         i == Xfake_acid_1 ? 0 :
7923                         i == Xfake_acid_2 ? 10 :
7924                         i == Xfake_acid_3 ? 20 :
7925                         i == Xfake_acid_4 ? 30 :
7926                         i == Xfake_acid_5 ? 40 :
7927                         i == Xfake_acid_6 ? 50 :
7928                         i == Xfake_acid_7 ? 60 :
7929                         i == Xfake_acid_8 ? 70 :
7930                         i == Xball_2 ? 7 :
7931                         i == Xball_2B ? j + 8 :
7932                         i == Yball_eat ? j + 1 :
7933                         i == Ykey_1_eat ? j + 1 :
7934                         i == Ykey_2_eat ? j + 1 :
7935                         i == Ykey_3_eat ? j + 1 :
7936                         i == Ykey_4_eat ? j + 1 :
7937                         i == Ykey_5_eat ? j + 1 :
7938                         i == Ykey_6_eat ? j + 1 :
7939                         i == Ykey_7_eat ? j + 1 :
7940                         i == Ykey_8_eat ? j + 1 :
7941                         i == Ylenses_eat ? j + 1 :
7942                         i == Ymagnify_eat ? j + 1 :
7943                         i == Ygrass_eat ? j + 1 :
7944                         i == Ydirt_eat ? j + 1 :
7945                         i == Xamoeba_1 ? 0 :
7946                         i == Xamoeba_2 ? 1 :
7947                         i == Xamoeba_3 ? 2 :
7948                         i == Xamoeba_4 ? 3 :
7949                         i == Xamoeba_5 ? 0 :
7950                         i == Xamoeba_6 ? 1 :
7951                         i == Xamoeba_7 ? 2 :
7952                         i == Xamoeba_8 ? 3 :
7953                         i == Xexit_2 ? j + 8 :
7954                         i == Xexit_3 ? j + 16 :
7955                         i == Xdynamite_1 ? 0 :
7956                         i == Xdynamite_2 ? 8 :
7957                         i == Xdynamite_3 ? 16 :
7958                         i == Xdynamite_4 ? 24 :
7959                         i == Xsand_stonein_1 ? j + 1 :
7960                         i == Xsand_stonein_2 ? j + 9 :
7961                         i == Xsand_stonein_3 ? j + 17 :
7962                         i == Xsand_stonein_4 ? j + 25 :
7963                         i == Xsand_stoneout_1 && j == 0 ? 0 :
7964                         i == Xsand_stoneout_1 && j == 1 ? 0 :
7965                         i == Xsand_stoneout_1 && j == 2 ? 1 :
7966                         i == Xsand_stoneout_1 && j == 3 ? 2 :
7967                         i == Xsand_stoneout_1 && j == 4 ? 2 :
7968                         i == Xsand_stoneout_1 && j == 5 ? 3 :
7969                         i == Xsand_stoneout_1 && j == 6 ? 4 :
7970                         i == Xsand_stoneout_1 && j == 7 ? 4 :
7971                         i == Xsand_stoneout_2 && j == 0 ? 5 :
7972                         i == Xsand_stoneout_2 && j == 1 ? 6 :
7973                         i == Xsand_stoneout_2 && j == 2 ? 7 :
7974                         i == Xsand_stoneout_2 && j == 3 ? 8 :
7975                         i == Xsand_stoneout_2 && j == 4 ? 9 :
7976                         i == Xsand_stoneout_2 && j == 5 ? 11 :
7977                         i == Xsand_stoneout_2 && j == 6 ? 13 :
7978                         i == Xsand_stoneout_2 && j == 7 ? 15 :
7979                         i == Xboom_bug && j == 1 ? 2 :
7980                         i == Xboom_bug && j == 2 ? 2 :
7981                         i == Xboom_bug && j == 3 ? 4 :
7982                         i == Xboom_bug && j == 4 ? 4 :
7983                         i == Xboom_bug && j == 5 ? 2 :
7984                         i == Xboom_bug && j == 6 ? 2 :
7985                         i == Xboom_bug && j == 7 ? 0 :
7986                         i == Xboom_bomb && j == 1 ? 2 :
7987                         i == Xboom_bomb && j == 2 ? 2 :
7988                         i == Xboom_bomb && j == 3 ? 4 :
7989                         i == Xboom_bomb && j == 4 ? 4 :
7990                         i == Xboom_bomb && j == 5 ? 2 :
7991                         i == Xboom_bomb && j == 6 ? 2 :
7992                         i == Xboom_bomb && j == 7 ? 0 :
7993                         i == Xboom_android && j == 7 ? 6 :
7994                         i == Xboom_1 && j == 1 ? 2 :
7995                         i == Xboom_1 && j == 2 ? 2 :
7996                         i == Xboom_1 && j == 3 ? 4 :
7997                         i == Xboom_1 && j == 4 ? 4 :
7998                         i == Xboom_1 && j == 5 ? 6 :
7999                         i == Xboom_1 && j == 6 ? 6 :
8000                         i == Xboom_1 && j == 7 ? 8 :
8001                         i == Xboom_2 && j == 0 ? 8 :
8002                         i == Xboom_2 && j == 1 ? 8 :
8003                         i == Xboom_2 && j == 2 ? 10 :
8004                         i == Xboom_2 && j == 3 ? 10 :
8005                         i == Xboom_2 && j == 4 ? 10 :
8006                         i == Xboom_2 && j == 5 ? 12 :
8007                         i == Xboom_2 && j == 6 ? 12 :
8008                         i == Xboom_2 && j == 7 ? 12 :
8009                         special_animation && j == 4 ? 3 :
8010                         effective_action != action ? 0 :
8011                         j);
8012
8013 #if DEBUG_EM_GFX
8014       Bitmap *debug_bitmap = g_em->bitmap;
8015       int debug_src_x = g_em->src_x;
8016       int debug_src_y = g_em->src_y;
8017 #endif
8018
8019       int frame = getAnimationFrame(g->anim_frames,
8020                                     g->anim_delay,
8021                                     g->anim_mode,
8022                                     g->anim_start_frame,
8023                                     sync_frame);
8024
8025       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8026                           g->double_movement && is_backside);
8027
8028       g_em->bitmap = src_bitmap;
8029       g_em->src_x = src_x;
8030       g_em->src_y = src_y;
8031       g_em->src_offset_x = 0;
8032       g_em->src_offset_y = 0;
8033       g_em->dst_offset_x = 0;
8034       g_em->dst_offset_y = 0;
8035       g_em->width  = TILEX;
8036       g_em->height = TILEY;
8037
8038       g_em->preserve_background = FALSE;
8039
8040       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8041                                sync_frame);
8042
8043       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8044                                    effective_action == ACTION_MOVING  ||
8045                                    effective_action == ACTION_PUSHING ||
8046                                    effective_action == ACTION_EATING)) ||
8047           (!has_action_graphics && (effective_action == ACTION_FILLING ||
8048                                     effective_action == ACTION_EMPTYING)))
8049       {
8050         int move_dir =
8051           (effective_action == ACTION_FALLING ||
8052            effective_action == ACTION_FILLING ||
8053            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8054         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8055         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
8056         int num_steps = (i == Ydrip_s1  ? 16 :
8057                          i == Ydrip_s1B ? 16 :
8058                          i == Ydrip_s2  ? 16 :
8059                          i == Ydrip_s2B ? 16 :
8060                          i == Xsand_stonein_1 ? 32 :
8061                          i == Xsand_stonein_2 ? 32 :
8062                          i == Xsand_stonein_3 ? 32 :
8063                          i == Xsand_stonein_4 ? 32 :
8064                          i == Xsand_stoneout_1 ? 16 :
8065                          i == Xsand_stoneout_2 ? 16 : 8);
8066         int cx = ABS(dx) * (TILEX / num_steps);
8067         int cy = ABS(dy) * (TILEY / num_steps);
8068         int step_frame = (i == Ydrip_s2         ? j + 8 :
8069                           i == Ydrip_s2B        ? j + 8 :
8070                           i == Xsand_stonein_2  ? j + 8 :
8071                           i == Xsand_stonein_3  ? j + 16 :
8072                           i == Xsand_stonein_4  ? j + 24 :
8073                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8074         int step = (is_backside ? step_frame : num_steps - step_frame);
8075
8076         if (is_backside)        /* tile where movement starts */
8077         {
8078           if (dx < 0 || dy < 0)
8079           {
8080             g_em->src_offset_x = cx * step;
8081             g_em->src_offset_y = cy * step;
8082           }
8083           else
8084           {
8085             g_em->dst_offset_x = cx * step;
8086             g_em->dst_offset_y = cy * step;
8087           }
8088         }
8089         else                    /* tile where movement ends */
8090         {
8091           if (dx < 0 || dy < 0)
8092           {
8093             g_em->dst_offset_x = cx * step;
8094             g_em->dst_offset_y = cy * step;
8095           }
8096           else
8097           {
8098             g_em->src_offset_x = cx * step;
8099             g_em->src_offset_y = cy * step;
8100           }
8101         }
8102
8103         g_em->width  = TILEX - cx * step;
8104         g_em->height = TILEY - cy * step;
8105       }
8106
8107       /* create unique graphic identifier to decide if tile must be redrawn */
8108       /* bit 31 - 16 (16 bit): EM style graphic
8109          bit 15 - 12 ( 4 bit): EM style frame
8110          bit 11 -  6 ( 6 bit): graphic width
8111          bit  5 -  0 ( 6 bit): graphic height */
8112       g_em->unique_identifier =
8113         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8114
8115 #if DEBUG_EM_GFX
8116
8117       /* skip check for EMC elements not contained in original EMC artwork */
8118       if (element == EL_EMC_FAKE_ACID)
8119         continue;
8120
8121       if (g_em->bitmap != debug_bitmap ||
8122           g_em->src_x != debug_src_x ||
8123           g_em->src_y != debug_src_y ||
8124           g_em->src_offset_x != 0 ||
8125           g_em->src_offset_y != 0 ||
8126           g_em->dst_offset_x != 0 ||
8127           g_em->dst_offset_y != 0 ||
8128           g_em->width != TILEX ||
8129           g_em->height != TILEY)
8130       {
8131         static int last_i = -1;
8132
8133         if (i != last_i)
8134         {
8135           printf("\n");
8136           last_i = i;
8137         }
8138
8139         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8140                i, element, element_info[element].token_name,
8141                element_action_info[effective_action].suffix, direction);
8142
8143         if (element != effective_element)
8144           printf(" [%d ('%s')]",
8145                  effective_element,
8146                  element_info[effective_element].token_name);
8147
8148         printf("\n");
8149
8150         if (g_em->bitmap != debug_bitmap)
8151           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8152                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8153
8154         if (g_em->src_x != debug_src_x ||
8155             g_em->src_y != debug_src_y)
8156           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8157                  j, (is_backside ? 'B' : 'F'),
8158                  g_em->src_x, g_em->src_y,
8159                  g_em->src_x / 32, g_em->src_y / 32,
8160                  debug_src_x, debug_src_y,
8161                  debug_src_x / 32, debug_src_y / 32);
8162
8163         if (g_em->src_offset_x != 0 ||
8164             g_em->src_offset_y != 0 ||
8165             g_em->dst_offset_x != 0 ||
8166             g_em->dst_offset_y != 0)
8167           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8168                  j, is_backside,
8169                  g_em->src_offset_x, g_em->src_offset_y,
8170                  g_em->dst_offset_x, g_em->dst_offset_y);
8171
8172         if (g_em->width != TILEX ||
8173             g_em->height != TILEY)
8174           printf("    %d (%d): size %d,%d should be %d,%d\n",
8175                  j, is_backside,
8176                  g_em->width, g_em->height, TILEX, TILEY);
8177
8178         num_em_gfx_errors++;
8179       }
8180 #endif
8181
8182     }
8183   }
8184
8185   for (i = 0; i < TILE_MAX; i++)
8186   {
8187     for (j = 0; j < 8; j++)
8188     {
8189       int element = object_mapping[i].element_rnd;
8190       int action = object_mapping[i].action;
8191       int direction = object_mapping[i].direction;
8192       boolean is_backside = object_mapping[i].is_backside;
8193       int graphic_action  = el_act_dir2img(element, action, direction);
8194       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8195
8196       if ((action == ACTION_SMASHED_BY_ROCK ||
8197            action == ACTION_SMASHED_BY_SPRING ||
8198            action == ACTION_EATING) &&
8199           graphic_action == graphic_default)
8200       {
8201         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
8202                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8203                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
8204                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8205                  Xspring);
8206
8207         /* no separate animation for "smashed by rock" -- use rock instead */
8208         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8209         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8210
8211         g_em->bitmap            = g_xx->bitmap;
8212         g_em->src_x             = g_xx->src_x;
8213         g_em->src_y             = g_xx->src_y;
8214         g_em->src_offset_x      = g_xx->src_offset_x;
8215         g_em->src_offset_y      = g_xx->src_offset_y;
8216         g_em->dst_offset_x      = g_xx->dst_offset_x;
8217         g_em->dst_offset_y      = g_xx->dst_offset_y;
8218         g_em->width             = g_xx->width;
8219         g_em->height            = g_xx->height;
8220         g_em->unique_identifier = g_xx->unique_identifier;
8221
8222         if (!is_backside)
8223           g_em->preserve_background = TRUE;
8224       }
8225     }
8226   }
8227
8228   for (p = 0; p < MAX_PLAYERS; p++)
8229   {
8230     for (i = 0; i < SPR_MAX; i++)
8231     {
8232       int element = player_mapping[p][i].element_rnd;
8233       int action = player_mapping[p][i].action;
8234       int direction = player_mapping[p][i].direction;
8235
8236       for (j = 0; j < 8; j++)
8237       {
8238         int effective_element = element;
8239         int effective_action = action;
8240         int graphic = (direction == MV_NONE ?
8241                        el_act2img(effective_element, effective_action) :
8242                        el_act_dir2img(effective_element, effective_action,
8243                                       direction));
8244         struct GraphicInfo *g = &graphic_info[graphic];
8245         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8246         Bitmap *src_bitmap;
8247         int src_x, src_y;
8248         int sync_frame = j;
8249
8250 #if DEBUG_EM_GFX
8251         Bitmap *debug_bitmap = g_em->bitmap;
8252         int debug_src_x = g_em->src_x;
8253         int debug_src_y = g_em->src_y;
8254 #endif
8255
8256         int frame = getAnimationFrame(g->anim_frames,
8257                                       g->anim_delay,
8258                                       g->anim_mode,
8259                                       g->anim_start_frame,
8260                                       sync_frame);
8261
8262         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8263
8264         g_em->bitmap = src_bitmap;
8265         g_em->src_x = src_x;
8266         g_em->src_y = src_y;
8267         g_em->src_offset_x = 0;
8268         g_em->src_offset_y = 0;
8269         g_em->dst_offset_x = 0;
8270         g_em->dst_offset_y = 0;
8271         g_em->width  = TILEX;
8272         g_em->height = TILEY;
8273
8274 #if DEBUG_EM_GFX
8275
8276         /* skip check for EMC elements not contained in original EMC artwork */
8277         if (element == EL_PLAYER_3 ||
8278             element == EL_PLAYER_4)
8279           continue;
8280
8281         if (g_em->bitmap != debug_bitmap ||
8282             g_em->src_x != debug_src_x ||
8283             g_em->src_y != debug_src_y)
8284         {
8285           static int last_i = -1;
8286
8287           if (i != last_i)
8288           {
8289             printf("\n");
8290             last_i = i;
8291           }
8292
8293           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8294                  p, i, element, element_info[element].token_name,
8295                  element_action_info[effective_action].suffix, direction);
8296
8297           if (element != effective_element)
8298             printf(" [%d ('%s')]",
8299                    effective_element,
8300                    element_info[effective_element].token_name);
8301
8302           printf("\n");
8303
8304           if (g_em->bitmap != debug_bitmap)
8305             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
8306                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
8307
8308           if (g_em->src_x != debug_src_x ||
8309               g_em->src_y != debug_src_y)
8310             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8311                    j,
8312                    g_em->src_x, g_em->src_y,
8313                    g_em->src_x / 32, g_em->src_y / 32,
8314                    debug_src_x, debug_src_y,
8315                    debug_src_x / 32, debug_src_y / 32);
8316
8317           num_em_gfx_errors++;
8318         }
8319 #endif
8320
8321       }
8322     }
8323   }
8324
8325 #if DEBUG_EM_GFX
8326   printf("\n");
8327   printf("::: [%d errors found]\n", num_em_gfx_errors);
8328
8329   exit(0);
8330 #endif
8331 }
8332
8333 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8334                                 boolean any_player_moving,
8335                                 boolean any_player_snapping,
8336                                 boolean any_player_dropping)
8337 {
8338   if (frame == 0 && !any_player_dropping)
8339   {
8340     if (!local_player->was_waiting)
8341     {
8342       if (!CheckSaveEngineSnapshotToList())
8343         return;
8344
8345       local_player->was_waiting = TRUE;
8346     }
8347   }
8348   else if (any_player_moving || any_player_snapping || any_player_dropping)
8349   {
8350     local_player->was_waiting = FALSE;
8351   }
8352 }
8353
8354 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8355                                 boolean murphy_is_dropping)
8356 {
8357   if (murphy_is_waiting)
8358   {
8359     if (!local_player->was_waiting)
8360     {
8361       if (!CheckSaveEngineSnapshotToList())
8362         return;
8363
8364       local_player->was_waiting = TRUE;
8365     }
8366   }
8367   else
8368   {
8369     local_player->was_waiting = FALSE;
8370   }
8371 }
8372
8373 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8374                             boolean any_player_moving,
8375                             boolean any_player_snapping,
8376                             boolean any_player_dropping)
8377 {
8378   if (tape.single_step && tape.recording && !tape.pausing)
8379     if (frame == 0 && !any_player_dropping)
8380       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8381
8382   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8383                              any_player_snapping, any_player_dropping);
8384 }
8385
8386 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8387                             boolean murphy_is_dropping)
8388 {
8389   boolean murphy_starts_dropping = FALSE;
8390   int i;
8391
8392   for (i = 0; i < MAX_PLAYERS; i++)
8393     if (stored_player[i].force_dropping)
8394       murphy_starts_dropping = TRUE;
8395
8396   if (tape.single_step && tape.recording && !tape.pausing)
8397     if (murphy_is_waiting && !murphy_starts_dropping)
8398       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8399
8400   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8401 }
8402
8403 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8404                          int graphic, int sync_frame, int x, int y)
8405 {
8406   int frame = getGraphicAnimationFrame(graphic, sync_frame);
8407
8408   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8409 }
8410
8411 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8412 {
8413   return (IS_NEXT_FRAME(sync_frame, graphic));
8414 }
8415
8416 int getGraphicInfo_Delay(int graphic)
8417 {
8418   return graphic_info[graphic].anim_delay;
8419 }
8420
8421 void PlayMenuSoundExt(int sound)
8422 {
8423   if (sound == SND_UNDEFINED)
8424     return;
8425
8426   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8427       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8428     return;
8429
8430   if (IS_LOOP_SOUND(sound))
8431     PlaySoundLoop(sound);
8432   else
8433     PlaySound(sound);
8434 }
8435
8436 void PlayMenuSound()
8437 {
8438   PlayMenuSoundExt(menu.sound[game_status]);
8439 }
8440
8441 void PlayMenuSoundStereo(int sound, int stereo_position)
8442 {
8443   if (sound == SND_UNDEFINED)
8444     return;
8445
8446   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8447       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8448     return;
8449
8450   if (IS_LOOP_SOUND(sound))
8451     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8452   else
8453     PlaySoundStereo(sound, stereo_position);
8454 }
8455
8456 void PlayMenuSoundIfLoopExt(int sound)
8457 {
8458   if (sound == SND_UNDEFINED)
8459     return;
8460
8461   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8462       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8463     return;
8464
8465   if (IS_LOOP_SOUND(sound))
8466     PlaySoundLoop(sound);
8467 }
8468
8469 void PlayMenuSoundIfLoop()
8470 {
8471   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8472 }
8473
8474 void PlayMenuMusicExt(int music)
8475 {
8476   if (music == MUS_UNDEFINED)
8477     return;
8478
8479   if (!setup.sound_music)
8480     return;
8481
8482   PlayMusic(music);
8483 }
8484
8485 void PlayMenuMusic()
8486 {
8487   char *curr_music = getCurrentlyPlayingMusicFilename();
8488   char *next_music = getMusicListEntry(menu.music[game_status])->filename;
8489
8490   if (!strEqual(curr_music, next_music))
8491     PlayMenuMusicExt(menu.music[game_status]);
8492 }
8493
8494 void PlayMenuSoundsAndMusic()
8495 {
8496   PlayMenuSound();
8497   PlayMenuMusic();
8498 }
8499
8500 static void FadeMenuSounds()
8501 {
8502   FadeSounds();
8503 }
8504
8505 static void FadeMenuMusic()
8506 {
8507   char *curr_music = getCurrentlyPlayingMusicFilename();
8508   char *next_music = getMusicListEntry(menu.music[game_status])->filename;
8509
8510   if (!strEqual(curr_music, next_music))
8511     FadeMusic();
8512 }
8513
8514 void FadeMenuSoundsAndMusic()
8515 {
8516   FadeMenuSounds();
8517   FadeMenuMusic();
8518 }
8519
8520 void PlaySoundActivating()
8521 {
8522 #if 0
8523   PlaySound(SND_MENU_ITEM_ACTIVATING);
8524 #endif
8525 }
8526
8527 void PlaySoundSelecting()
8528 {
8529 #if 0
8530   PlaySound(SND_MENU_ITEM_SELECTING);
8531 #endif
8532 }
8533
8534 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8535 {
8536   boolean change_fullscreen = (setup.fullscreen !=
8537                                video.fullscreen_enabled);
8538   boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8539                                            setup.window_scaling_percent !=
8540                                            video.window_scaling_percent);
8541
8542   if (change_window_scaling_percent && video.fullscreen_enabled)
8543     return;
8544
8545   if (!change_window_scaling_percent && !video.fullscreen_available)
8546     return;
8547
8548 #if defined(TARGET_SDL2)
8549   if (change_window_scaling_percent)
8550   {
8551     SDLSetWindowScaling(setup.window_scaling_percent);
8552
8553     return;
8554   }
8555   else if (change_fullscreen)
8556   {
8557     SDLSetWindowFullscreen(setup.fullscreen);
8558
8559     /* set setup value according to successfully changed fullscreen mode */
8560     setup.fullscreen = video.fullscreen_enabled;
8561
8562     return;
8563   }
8564 #endif
8565
8566   if (change_fullscreen ||
8567       change_window_scaling_percent)
8568   {
8569     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8570
8571     /* save backbuffer content which gets lost when toggling fullscreen mode */
8572     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8573
8574     if (change_window_scaling_percent)
8575     {
8576       /* keep window mode, but change window scaling */
8577       video.fullscreen_enabled = TRUE;          /* force new window scaling */
8578     }
8579
8580     /* toggle fullscreen */
8581     ChangeVideoModeIfNeeded(setup.fullscreen);
8582
8583     /* set setup value according to successfully changed fullscreen mode */
8584     setup.fullscreen = video.fullscreen_enabled;
8585
8586     /* restore backbuffer content from temporary backbuffer backup bitmap */
8587     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8588
8589     FreeBitmap(tmp_backbuffer);
8590
8591     /* update visible window/screen */
8592     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8593   }
8594 }
8595
8596 void JoinRectangles(int *x, int *y, int *width, int *height,
8597                     int x2, int y2, int width2, int height2)
8598 {
8599   // do not join with "off-screen" rectangle
8600   if (x2 == -1 || y2 == -1)
8601     return;
8602
8603   *x = MIN(*x, x2);
8604   *y = MIN(*y, y2);
8605   *width = MAX(*width, width2);
8606   *height = MAX(*height, height2);
8607 }
8608
8609 void SetAnimStatus(int anim_status_new)
8610 {
8611   if (anim_status_new == GAME_MODE_MAIN)
8612     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8613   else if (anim_status_new == GAME_MODE_SCORES)
8614     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8615
8616   global.anim_status_next = anim_status_new;
8617
8618   // directly set screen modes that are entered without fading
8619   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
8620        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8621       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
8622        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8623     global.anim_status = global.anim_status_next;
8624 }
8625
8626 void SetGameStatus(int game_status_new)
8627 {
8628   if (game_status_new != game_status)
8629     game_status_last_screen = game_status;
8630
8631   game_status = game_status_new;
8632
8633   SetAnimStatus(game_status_new);
8634 }
8635
8636 void SetFontStatus(int game_status_new)
8637 {
8638   static int last_game_status = -1;
8639
8640   if (game_status_new != -1)
8641   {
8642     // set game status for font use after storing last game status
8643     last_game_status = game_status;
8644     game_status = game_status_new;
8645   }
8646   else
8647   {
8648     // reset game status after font use from last stored game status
8649     game_status = last_game_status;
8650   }
8651 }
8652
8653 void ResetFontStatus()
8654 {
8655   SetFontStatus(-1);
8656 }
8657
8658 void ChangeViewportPropertiesIfNeeded()
8659 {
8660   int gfx_game_mode = game_status;
8661   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8662                         game_status);
8663   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
8664   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8665   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
8666   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
8667   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
8668   int new_win_xsize     = vp_window->width;
8669   int new_win_ysize     = vp_window->height;
8670   int border_size       = vp_playfield->border_size;
8671   int new_sx            = vp_playfield->x + border_size;
8672   int new_sy            = vp_playfield->y + border_size;
8673   int new_sxsize        = vp_playfield->width  - 2 * border_size;
8674   int new_sysize        = vp_playfield->height - 2 * border_size;
8675   int new_real_sx       = vp_playfield->x;
8676   int new_real_sy       = vp_playfield->y;
8677   int new_full_sxsize   = vp_playfield->width;
8678   int new_full_sysize   = vp_playfield->height;
8679   int new_dx            = vp_door_1->x;
8680   int new_dy            = vp_door_1->y;
8681   int new_dxsize        = vp_door_1->width;
8682   int new_dysize        = vp_door_1->height;
8683   int new_vx            = vp_door_2->x;
8684   int new_vy            = vp_door_2->y;
8685   int new_vxsize        = vp_door_2->width;
8686   int new_vysize        = vp_door_2->height;
8687   int new_ex            = vp_door_3->x;
8688   int new_ey            = vp_door_3->y;
8689   int new_exsize        = vp_door_3->width;
8690   int new_eysize        = vp_door_3->height;
8691   int new_tilesize_var =
8692     (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8693
8694   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8695                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8696   int new_scr_fieldx = new_sxsize / tilesize;
8697   int new_scr_fieldy = new_sysize / tilesize;
8698   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8699   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8700   boolean init_gfx_buffers = FALSE;
8701   boolean init_video_buffer = FALSE;
8702   boolean init_gadgets_and_anims = FALSE;
8703   boolean init_em_graphics = FALSE;
8704
8705   if (new_win_xsize != WIN_XSIZE ||
8706       new_win_ysize != WIN_YSIZE)
8707   {
8708     WIN_XSIZE = new_win_xsize;
8709     WIN_YSIZE = new_win_ysize;
8710
8711     init_video_buffer = TRUE;
8712     init_gfx_buffers = TRUE;
8713     init_gadgets_and_anims = TRUE;
8714
8715     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8716   }
8717
8718   if (new_scr_fieldx != SCR_FIELDX ||
8719       new_scr_fieldy != SCR_FIELDY)
8720   {
8721     /* this always toggles between MAIN and GAME when using small tile size */
8722
8723     SCR_FIELDX = new_scr_fieldx;
8724     SCR_FIELDY = new_scr_fieldy;
8725
8726     // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8727   }
8728
8729   if (new_sx != SX ||
8730       new_sy != SY ||
8731       new_dx != DX ||
8732       new_dy != DY ||
8733       new_vx != VX ||
8734       new_vy != VY ||
8735       new_ex != EX ||
8736       new_ey != EY ||
8737       new_sxsize != SXSIZE ||
8738       new_sysize != SYSIZE ||
8739       new_dxsize != DXSIZE ||
8740       new_dysize != DYSIZE ||
8741       new_vxsize != VXSIZE ||
8742       new_vysize != VYSIZE ||
8743       new_exsize != EXSIZE ||
8744       new_eysize != EYSIZE ||
8745       new_real_sx != REAL_SX ||
8746       new_real_sy != REAL_SY ||
8747       new_full_sxsize != FULL_SXSIZE ||
8748       new_full_sysize != FULL_SYSIZE ||
8749       new_tilesize_var != TILESIZE_VAR
8750       )
8751   {
8752     // ------------------------------------------------------------------------
8753     // determine next fading area for changed viewport definitions
8754     // ------------------------------------------------------------------------
8755
8756     // start with current playfield area (default fading area)
8757     FADE_SX = REAL_SX;
8758     FADE_SY = REAL_SY;
8759     FADE_SXSIZE = FULL_SXSIZE;
8760     FADE_SYSIZE = FULL_SYSIZE;
8761
8762     // add new playfield area if position or size has changed
8763     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8764         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8765     {
8766       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8767                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8768     }
8769
8770     // add current and new door 1 area if position or size has changed
8771     if (new_dx != DX || new_dy != DY ||
8772         new_dxsize != DXSIZE || new_dysize != DYSIZE)
8773     {
8774       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8775                      DX, DY, DXSIZE, DYSIZE);
8776       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8777                      new_dx, new_dy, new_dxsize, new_dysize);
8778     }
8779
8780     // add current and new door 2 area if position or size has changed
8781     if (new_dx != VX || new_dy != VY ||
8782         new_dxsize != VXSIZE || new_dysize != VYSIZE)
8783     {
8784       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8785                      VX, VY, VXSIZE, VYSIZE);
8786       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8787                      new_vx, new_vy, new_vxsize, new_vysize);
8788     }
8789
8790     // ------------------------------------------------------------------------
8791     // handle changed tile size
8792     // ------------------------------------------------------------------------
8793
8794     if (new_tilesize_var != TILESIZE_VAR)
8795     {
8796       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8797
8798       // changing tile size invalidates scroll values of engine snapshots
8799       FreeEngineSnapshotSingle();
8800
8801       // changing tile size requires update of graphic mapping for EM engine
8802       init_em_graphics = TRUE;
8803     }
8804
8805     SX = new_sx;
8806     SY = new_sy;
8807     DX = new_dx;
8808     DY = new_dy;
8809     VX = new_vx;
8810     VY = new_vy;
8811     EX = new_ex;
8812     EY = new_ey;
8813     SXSIZE = new_sxsize;
8814     SYSIZE = new_sysize;
8815     DXSIZE = new_dxsize;
8816     DYSIZE = new_dysize;
8817     VXSIZE = new_vxsize;
8818     VYSIZE = new_vysize;
8819     EXSIZE = new_exsize;
8820     EYSIZE = new_eysize;
8821     REAL_SX = new_real_sx;
8822     REAL_SY = new_real_sy;
8823     FULL_SXSIZE = new_full_sxsize;
8824     FULL_SYSIZE = new_full_sysize;
8825     TILESIZE_VAR = new_tilesize_var;
8826
8827     init_gfx_buffers = TRUE;
8828     init_gadgets_and_anims = TRUE;
8829
8830     // printf("::: viewports: init_gfx_buffers\n");
8831     // printf("::: viewports: init_gadgets_and_anims\n");
8832   }
8833
8834   if (init_gfx_buffers)
8835   {
8836     // printf("::: init_gfx_buffers\n");
8837
8838     SCR_FIELDX = new_scr_fieldx_buffers;
8839     SCR_FIELDY = new_scr_fieldy_buffers;
8840
8841     InitGfxBuffers();
8842
8843     SCR_FIELDX = new_scr_fieldx;
8844     SCR_FIELDY = new_scr_fieldy;
8845
8846     SetDrawDeactivationMask(REDRAW_NONE);
8847     SetDrawBackgroundMask(REDRAW_FIELD);
8848   }
8849
8850   if (init_video_buffer)
8851   {
8852     // printf("::: init_video_buffer\n");
8853
8854     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8855     InitImageTextures();
8856   }
8857
8858   if (init_gadgets_and_anims)
8859   {
8860     // printf("::: init_gadgets_and_anims\n");
8861
8862     InitGadgets();
8863     InitGlobalAnimations();
8864   }
8865
8866   if (init_em_graphics)
8867   {
8868       InitGraphicInfo_EM();
8869   }
8870 }