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