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