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