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