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