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