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