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