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