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