added support for cursor keys and joystick to network player request dialog
[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 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3518                                            int graphic, int sync_frame,
3519                                            int mask_mode)
3520 {
3521   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3522
3523   if (mask_mode == USE_MASKING)
3524     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3525   else
3526     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3527 }
3528
3529 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3530                                   int graphic, int sync_frame, int mask_mode)
3531 {
3532   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3533
3534   if (mask_mode == USE_MASKING)
3535     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3536   else
3537     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3538 }
3539
3540 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3541 {
3542   int lx = LEVELX(x), ly = LEVELY(y);
3543
3544   if (!IN_SCR_FIELD(x, y))
3545     return;
3546
3547   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3548                           graphic, GfxFrame[lx][ly], NO_MASKING);
3549
3550   MarkTileDirty(x, y);
3551 }
3552
3553 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3554 {
3555   int lx = LEVELX(x), ly = LEVELY(y);
3556
3557   if (!IN_SCR_FIELD(x, y))
3558     return;
3559
3560   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3561                           graphic, GfxFrame[lx][ly], NO_MASKING);
3562   MarkTileDirty(x, y);
3563 }
3564
3565 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3566 {
3567   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3568 }
3569
3570 void DrawLevelElementAnimation(int x, int y, int element)
3571 {
3572   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3573
3574   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3575 }
3576
3577 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3578 {
3579   int sx = SCREENX(x), sy = SCREENY(y);
3580
3581   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3582     return;
3583
3584   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3585     return;
3586
3587   DrawGraphicAnimation(sx, sy, graphic);
3588
3589 #if 1
3590   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3591     DrawLevelFieldCrumbled(x, y);
3592 #else
3593   if (GFX_CRUMBLED(Feld[x][y]))
3594     DrawLevelFieldCrumbled(x, y);
3595 #endif
3596 }
3597
3598 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3599 {
3600   int sx = SCREENX(x), sy = SCREENY(y);
3601   int graphic;
3602
3603   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3604     return;
3605
3606   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3607
3608   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3609     return;
3610
3611   DrawGraphicAnimation(sx, sy, graphic);
3612
3613   if (GFX_CRUMBLED(element))
3614     DrawLevelFieldCrumbled(x, y);
3615 }
3616
3617 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3618 {
3619   if (player->use_murphy)
3620   {
3621     /* this works only because currently only one player can be "murphy" ... */
3622     static int last_horizontal_dir = MV_LEFT;
3623     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3624
3625     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3626       last_horizontal_dir = move_dir;
3627
3628     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
3629     {
3630       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3631
3632       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3633     }
3634
3635     return graphic;
3636   }
3637   else
3638     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3639 }
3640
3641 static boolean equalGraphics(int graphic1, int graphic2)
3642 {
3643   struct GraphicInfo *g1 = &graphic_info[graphic1];
3644   struct GraphicInfo *g2 = &graphic_info[graphic2];
3645
3646   return (g1->bitmap      == g2->bitmap &&
3647           g1->src_x       == g2->src_x &&
3648           g1->src_y       == g2->src_y &&
3649           g1->anim_frames == g2->anim_frames &&
3650           g1->anim_delay  == g2->anim_delay &&
3651           g1->anim_mode   == g2->anim_mode);
3652 }
3653
3654 void DrawAllPlayers()
3655 {
3656   int i;
3657
3658   for (i = 0; i < MAX_PLAYERS; i++)
3659     if (stored_player[i].active)
3660       DrawPlayer(&stored_player[i]);
3661 }
3662
3663 void DrawPlayerField(int x, int y)
3664 {
3665   if (!IS_PLAYER(x, y))
3666     return;
3667
3668   DrawPlayer(PLAYERINFO(x, y));
3669 }
3670
3671 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3672
3673 void DrawPlayer(struct PlayerInfo *player)
3674 {
3675   int jx = player->jx;
3676   int jy = player->jy;
3677   int move_dir = player->MovDir;
3678   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3679   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
3680   int last_jx = (player->is_moving ? jx - dx : jx);
3681   int last_jy = (player->is_moving ? jy - dy : jy);
3682   int next_jx = jx + dx;
3683   int next_jy = jy + dy;
3684   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3685   boolean player_is_opaque = FALSE;
3686   int sx = SCREENX(jx), sy = SCREENY(jy);
3687   int sxx = 0, syy = 0;
3688   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3689   int graphic;
3690   int action = ACTION_DEFAULT;
3691   int last_player_graphic = getPlayerGraphic(player, move_dir);
3692   int last_player_frame = player->Frame;
3693   int frame = 0;
3694
3695   /* GfxElement[][] is set to the element the player is digging or collecting;
3696      remove also for off-screen player if the player is not moving anymore */
3697   if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3698     GfxElement[jx][jy] = EL_UNDEFINED;
3699
3700   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3701     return;
3702
3703 #if DEBUG
3704   if (!IN_LEV_FIELD(jx, jy))
3705   {
3706     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3707     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3708     printf("DrawPlayerField(): This should never happen!\n");
3709     return;
3710   }
3711 #endif
3712
3713   if (element == EL_EXPLOSION)
3714     return;
3715
3716   action = (player->is_pushing    ? ACTION_PUSHING         :
3717             player->is_digging    ? ACTION_DIGGING         :
3718             player->is_collecting ? ACTION_COLLECTING      :
3719             player->is_moving     ? ACTION_MOVING          :
3720             player->is_snapping   ? ACTION_SNAPPING        :
3721             player->is_dropping   ? ACTION_DROPPING        :
3722             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
3723
3724   if (player->is_waiting)
3725     move_dir = player->dir_waiting;
3726
3727   InitPlayerGfxAnimation(player, action, move_dir);
3728
3729   /* ----------------------------------------------------------------------- */
3730   /* draw things in the field the player is leaving, if needed               */
3731   /* ----------------------------------------------------------------------- */
3732
3733   if (player->is_moving)
3734   {
3735     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3736     {
3737       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3738
3739       if (last_element == EL_DYNAMITE_ACTIVE ||
3740           last_element == EL_EM_DYNAMITE_ACTIVE ||
3741           last_element == EL_SP_DISK_RED_ACTIVE)
3742         DrawDynamite(last_jx, last_jy);
3743       else
3744         DrawLevelFieldThruMask(last_jx, last_jy);
3745     }
3746     else if (last_element == EL_DYNAMITE_ACTIVE ||
3747              last_element == EL_EM_DYNAMITE_ACTIVE ||
3748              last_element == EL_SP_DISK_RED_ACTIVE)
3749       DrawDynamite(last_jx, last_jy);
3750 #if 0
3751     /* !!! this is not enough to prevent flickering of players which are
3752        moving next to each others without a free tile between them -- this
3753        can only be solved by drawing all players layer by layer (first the
3754        background, then the foreground etc.) !!! => TODO */
3755     else if (!IS_PLAYER(last_jx, last_jy))
3756       DrawLevelField(last_jx, last_jy);
3757 #else
3758     else
3759       DrawLevelField(last_jx, last_jy);
3760 #endif
3761
3762     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3763       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3764   }
3765
3766   if (!IN_SCR_FIELD(sx, sy))
3767     return;
3768
3769   /* ----------------------------------------------------------------------- */
3770   /* draw things behind the player, if needed                                */
3771   /* ----------------------------------------------------------------------- */
3772
3773   if (Back[jx][jy])
3774     DrawLevelElement(jx, jy, Back[jx][jy]);
3775   else if (IS_ACTIVE_BOMB(element))
3776     DrawLevelElement(jx, jy, EL_EMPTY);
3777   else
3778   {
3779     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3780     {
3781       int old_element = GfxElement[jx][jy];
3782       int old_graphic = el_act_dir2img(old_element, action, move_dir);
3783       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3784
3785       if (GFX_CRUMBLED(old_element))
3786         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3787       else
3788         DrawGraphic(sx, sy, old_graphic, frame);
3789
3790       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3791         player_is_opaque = TRUE;
3792     }
3793     else
3794     {
3795       GfxElement[jx][jy] = EL_UNDEFINED;
3796
3797       /* make sure that pushed elements are drawn with correct frame rate */
3798       graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3799
3800       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3801         GfxFrame[jx][jy] = player->StepFrame;
3802
3803       DrawLevelField(jx, jy);
3804     }
3805   }
3806
3807 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3808   /* ----------------------------------------------------------------------- */
3809   /* draw player himself                                                     */
3810   /* ----------------------------------------------------------------------- */
3811
3812   graphic = getPlayerGraphic(player, move_dir);
3813
3814   /* in the case of changed player action or direction, prevent the current
3815      animation frame from being restarted for identical animations */
3816   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3817     player->Frame = last_player_frame;
3818
3819   frame = getGraphicAnimationFrame(graphic, player->Frame);
3820
3821   if (player->GfxPos)
3822   {
3823     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3824       sxx = player->GfxPos;
3825     else
3826       syy = player->GfxPos;
3827   }
3828
3829   if (player_is_opaque)
3830     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3831   else
3832     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3833
3834   if (SHIELD_ON(player))
3835   {
3836     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3837                    IMG_SHIELD_NORMAL_ACTIVE);
3838     int frame = getGraphicAnimationFrame(graphic, -1);
3839
3840     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3841   }
3842 #endif
3843
3844 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3845   if (player->GfxPos)
3846   {
3847     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3848       sxx = player->GfxPos;
3849     else
3850       syy = player->GfxPos;
3851   }
3852 #endif
3853
3854   /* ----------------------------------------------------------------------- */
3855   /* draw things the player is pushing, if needed                            */
3856   /* ----------------------------------------------------------------------- */
3857
3858   if (player->is_pushing && player->is_moving)
3859   {
3860     int px = SCREENX(jx), py = SCREENY(jy);
3861     int pxx = (TILEX - ABS(sxx)) * dx;
3862     int pyy = (TILEY - ABS(syy)) * dy;
3863     int gfx_frame = GfxFrame[jx][jy];
3864
3865     int graphic;
3866     int sync_frame;
3867     int frame;
3868
3869     if (!IS_MOVING(jx, jy))             /* push movement already finished */
3870     {
3871       element = Feld[next_jx][next_jy];
3872       gfx_frame = GfxFrame[next_jx][next_jy];
3873     }
3874
3875     graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3876
3877     sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3878     frame = getGraphicAnimationFrame(graphic, sync_frame);
3879
3880     /* draw background element under pushed element (like the Sokoban field) */
3881     if (game.use_masked_pushing && IS_MOVING(jx, jy))
3882     {
3883       /* this allows transparent pushing animation over non-black background */
3884
3885       if (Back[jx][jy])
3886         DrawLevelElement(jx, jy, Back[jx][jy]);
3887       else
3888         DrawLevelElement(jx, jy, EL_EMPTY);
3889
3890       if (Back[next_jx][next_jy])
3891         DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3892       else
3893         DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3894     }
3895     else if (Back[next_jx][next_jy])
3896       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3897
3898 #if 1
3899     /* do not draw (EM style) pushing animation when pushing is finished */
3900     /* (two-tile animations usually do not contain start and end frame) */
3901     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3902       DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3903     else
3904       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3905 #else
3906     /* masked drawing is needed for EMC style (double) movement graphics */
3907     /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3908     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3909 #endif
3910   }
3911
3912 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3913   /* ----------------------------------------------------------------------- */
3914   /* draw player himself                                                     */
3915   /* ----------------------------------------------------------------------- */
3916
3917   graphic = getPlayerGraphic(player, move_dir);
3918
3919   /* in the case of changed player action or direction, prevent the current
3920      animation frame from being restarted for identical animations */
3921   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3922     player->Frame = last_player_frame;
3923
3924   frame = getGraphicAnimationFrame(graphic, player->Frame);
3925
3926   if (player->GfxPos)
3927   {
3928     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3929       sxx = player->GfxPos;
3930     else
3931       syy = player->GfxPos;
3932   }
3933
3934   if (player_is_opaque)
3935     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3936   else
3937     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3938
3939   if (SHIELD_ON(player))
3940   {
3941     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3942                    IMG_SHIELD_NORMAL_ACTIVE);
3943     int frame = getGraphicAnimationFrame(graphic, -1);
3944
3945     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3946   }
3947 #endif
3948
3949   /* ----------------------------------------------------------------------- */
3950   /* draw things in front of player (active dynamite or dynabombs)           */
3951   /* ----------------------------------------------------------------------- */
3952
3953   if (IS_ACTIVE_BOMB(element))
3954   {
3955     graphic = el2img(element);
3956     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3957
3958     if (game.emulation == EMU_SUPAPLEX)
3959       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3960     else
3961       DrawGraphicThruMask(sx, sy, graphic, frame);
3962   }
3963
3964   if (player_is_moving && last_element == EL_EXPLOSION)
3965   {
3966     int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3967                    GfxElement[last_jx][last_jy] :  EL_EMPTY);
3968     int graphic = el_act2img(element, ACTION_EXPLODING);
3969     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3970     int phase = ExplodePhase[last_jx][last_jy] - 1;
3971     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3972
3973     if (phase >= delay)
3974       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3975   }
3976
3977   /* ----------------------------------------------------------------------- */
3978   /* draw elements the player is just walking/passing through/under          */
3979   /* ----------------------------------------------------------------------- */
3980
3981   if (player_is_moving)
3982   {
3983     /* handle the field the player is leaving ... */
3984     if (IS_ACCESSIBLE_INSIDE(last_element))
3985       DrawLevelField(last_jx, last_jy);
3986     else if (IS_ACCESSIBLE_UNDER(last_element))
3987       DrawLevelFieldThruMask(last_jx, last_jy);
3988   }
3989
3990   /* do not redraw accessible elements if the player is just pushing them */
3991   if (!player_is_moving || !player->is_pushing)
3992   {
3993     /* ... and the field the player is entering */
3994     if (IS_ACCESSIBLE_INSIDE(element))
3995       DrawLevelField(jx, jy);
3996     else if (IS_ACCESSIBLE_UNDER(element))
3997       DrawLevelFieldThruMask(jx, jy);
3998   }
3999
4000   MarkTileDirty(sx, sy);
4001 }
4002
4003 /* ------------------------------------------------------------------------- */
4004
4005 void WaitForEventToContinue()
4006 {
4007   boolean still_wait = TRUE;
4008
4009   if (program.headless)
4010     return;
4011
4012   /* simulate releasing mouse button over last gadget, if still pressed */
4013   if (button_status)
4014     HandleGadgets(-1, -1, 0);
4015
4016   button_status = MB_RELEASED;
4017
4018   ClearEventQueue();
4019
4020   while (still_wait)
4021   {
4022     Event event;
4023
4024     if (NextValidEvent(&event))
4025     {
4026       switch (event.type)
4027       {
4028         case EVENT_BUTTONPRESS:
4029         case EVENT_KEYPRESS:
4030 #if defined(TARGET_SDL2)
4031         case SDL_CONTROLLERBUTTONDOWN:
4032 #endif
4033         case SDL_JOYBUTTONDOWN:
4034           still_wait = FALSE;
4035           break;
4036
4037         case EVENT_KEYRELEASE:
4038           ClearPlayerAction();
4039           break;
4040
4041         default:
4042           HandleOtherEvents(&event);
4043           break;
4044       }
4045     }
4046     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4047     {
4048       still_wait = FALSE;
4049     }
4050
4051     BackToFront();
4052   }
4053 }
4054
4055 #define MAX_REQUEST_LINES               13
4056 #define MAX_REQUEST_LINE_FONT1_LEN      7
4057 #define MAX_REQUEST_LINE_FONT2_LEN      10
4058
4059 static int RequestHandleEvents(unsigned int req_state)
4060 {
4061   boolean level_solved = (game_status == GAME_MODE_PLAYING &&
4062                           local_player->LevelSolved_GameEnd);
4063   int width  = request.width;
4064   int height = request.height;
4065   int sx, sy;
4066   int result;
4067
4068   setRequestPosition(&sx, &sy, FALSE);
4069
4070   button_status = MB_RELEASED;
4071
4072   request_gadget_id = -1;
4073   result = -1;
4074
4075   while (result < 0)
4076   {
4077     if (level_solved)
4078     {
4079       /* the MM game engine does not use a special (scrollable) field buffer */
4080       if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4081         SetDrawtoField(DRAW_TO_FIELDBUFFER);
4082
4083       HandleGameActions();
4084
4085       SetDrawtoField(DRAW_TO_BACKBUFFER);
4086
4087       if (global.use_envelope_request)
4088       {
4089         /* copy current state of request area to middle of playfield area */
4090         BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4091       }
4092     }
4093
4094     if (PendingEvent())
4095     {
4096       Event event;
4097
4098       while (NextValidEvent(&event))
4099       {
4100         switch (event.type)
4101         {
4102           case EVENT_BUTTONPRESS:
4103           case EVENT_BUTTONRELEASE:
4104           case EVENT_MOTIONNOTIFY:
4105           {
4106             int mx, my;
4107
4108             if (event.type == EVENT_MOTIONNOTIFY)
4109             {
4110               if (!button_status)
4111                 continue;
4112
4113               motion_status = TRUE;
4114               mx = ((MotionEvent *) &event)->x;
4115               my = ((MotionEvent *) &event)->y;
4116             }
4117             else
4118             {
4119               motion_status = FALSE;
4120               mx = ((ButtonEvent *) &event)->x;
4121               my = ((ButtonEvent *) &event)->y;
4122               if (event.type == EVENT_BUTTONPRESS)
4123                 button_status = ((ButtonEvent *) &event)->button;
4124               else
4125                 button_status = MB_RELEASED;
4126             }
4127
4128             /* this sets 'request_gadget_id' */
4129             HandleGadgets(mx, my, button_status);
4130
4131             switch (request_gadget_id)
4132             {
4133               case TOOL_CTRL_ID_YES:
4134                 result = TRUE;
4135                 break;
4136               case TOOL_CTRL_ID_NO:
4137                 result = FALSE;
4138                 break;
4139               case TOOL_CTRL_ID_CONFIRM:
4140                 result = TRUE | FALSE;
4141                 break;
4142
4143               case TOOL_CTRL_ID_PLAYER_1:
4144                 result = 1;
4145                 break;
4146               case TOOL_CTRL_ID_PLAYER_2:
4147                 result = 2;
4148                 break;
4149               case TOOL_CTRL_ID_PLAYER_3:
4150                 result = 3;
4151                 break;
4152               case TOOL_CTRL_ID_PLAYER_4:
4153                 result = 4;
4154                 break;
4155
4156               default:
4157                 break;
4158             }
4159
4160             break;
4161           }
4162
4163 #if defined(TARGET_SDL2)
4164           case SDL_WINDOWEVENT:
4165             HandleWindowEvent((WindowEvent *) &event);
4166             break;
4167
4168           case SDL_APP_WILLENTERBACKGROUND:
4169           case SDL_APP_DIDENTERBACKGROUND:
4170           case SDL_APP_WILLENTERFOREGROUND:
4171           case SDL_APP_DIDENTERFOREGROUND:
4172             HandlePauseResumeEvent((PauseResumeEvent *) &event);
4173             break;
4174 #endif
4175
4176           case EVENT_KEYPRESS:
4177           {
4178             Key key = GetEventKey((KeyEvent *)&event, TRUE);
4179
4180             switch (key)
4181             {
4182               case KSYM_space:
4183                 if (req_state & REQ_CONFIRM)
4184                   result = 1;
4185                 break;
4186
4187               case KSYM_Return:
4188               case KSYM_y:
4189 #if defined(TARGET_SDL2)
4190               case KSYM_Y:
4191               case KSYM_Select:
4192               case KSYM_Menu:
4193 #if defined(KSYM_Rewind)
4194               case KSYM_Rewind:         /* for Amazon Fire TV remote */
4195 #endif
4196 #endif
4197                 result = 1;
4198                 break;
4199
4200               case KSYM_Escape:
4201               case KSYM_n:
4202 #if defined(TARGET_SDL2)
4203               case KSYM_N:
4204               case KSYM_Back:
4205 #if defined(KSYM_FastForward)
4206               case KSYM_FastForward:    /* for Amazon Fire TV remote */
4207 #endif
4208 #endif
4209                 result = 0;
4210                 break;
4211
4212               default:
4213                 HandleKeysDebug(key);
4214                 break;
4215             }
4216
4217             if (req_state & REQ_PLAYER)
4218             {
4219               int old_player_nr = setup.network_player_nr;
4220
4221               if (result != -1)
4222                 result = old_player_nr + 1;
4223
4224               switch (key)
4225               {
4226                 case KSYM_space:
4227                   result = old_player_nr + 1;
4228                   break;
4229
4230                 case KSYM_Up:
4231                 case KSYM_1:
4232                   result = 1;
4233                   break;
4234
4235                 case KSYM_Right:
4236                 case KSYM_2:
4237                   result = 2;
4238                   break;
4239
4240                 case KSYM_Down:
4241                 case KSYM_3:
4242                   result = 3;
4243                   break;
4244
4245                 case KSYM_Left:
4246                 case KSYM_4:
4247                   result = 4;
4248                   break;
4249
4250                 default:
4251                   break;
4252               }
4253             }
4254
4255             break;
4256           }
4257
4258           case EVENT_KEYRELEASE:
4259             ClearPlayerAction();
4260             break;
4261
4262 #if defined(TARGET_SDL2)
4263           case SDL_CONTROLLERBUTTONDOWN:
4264             switch (event.cbutton.button)
4265             {
4266               case SDL_CONTROLLER_BUTTON_A:
4267               case SDL_CONTROLLER_BUTTON_X:
4268               case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4269               case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4270                 result = 1;
4271                 break;
4272
4273               case SDL_CONTROLLER_BUTTON_B:
4274               case SDL_CONTROLLER_BUTTON_Y:
4275               case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4276               case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4277               case SDL_CONTROLLER_BUTTON_BACK:
4278                 result = 0;
4279                 break;
4280             }
4281
4282             if (req_state & REQ_PLAYER)
4283             {
4284               int old_player_nr = setup.network_player_nr;
4285
4286               if (result != -1)
4287                 result = old_player_nr + 1;
4288
4289               switch (event.cbutton.button)
4290               {
4291                 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4292                 case SDL_CONTROLLER_BUTTON_Y:
4293                   result = 1;
4294                   break;
4295
4296                 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4297                 case SDL_CONTROLLER_BUTTON_B:
4298                   result = 2;
4299                   break;
4300
4301                 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4302                 case SDL_CONTROLLER_BUTTON_A:
4303                   result = 3;
4304                   break;
4305
4306                 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4307                 case SDL_CONTROLLER_BUTTON_X:
4308                   result = 4;
4309                   break;
4310
4311                 default:
4312                   break;
4313               }
4314             }
4315
4316             break;
4317
4318           case SDL_CONTROLLERBUTTONUP:
4319             HandleJoystickEvent(&event);
4320             ClearPlayerAction();
4321             break;
4322 #endif
4323
4324           default:
4325             HandleOtherEvents(&event);
4326             break;
4327         }
4328       }
4329     }
4330     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4331     {
4332       int joy = AnyJoystick();
4333
4334       if (joy & JOY_BUTTON_1)
4335         result = 1;
4336       else if (joy & JOY_BUTTON_2)
4337         result = 0;
4338     }
4339     else if (AnyJoystick())
4340     {
4341       int joy = AnyJoystick();
4342
4343       if (req_state & REQ_PLAYER)
4344       {
4345         if (joy & JOY_UP)
4346           result = 1;
4347         else if (joy & JOY_RIGHT)
4348           result = 2;
4349         else if (joy & JOY_DOWN)
4350           result = 3;
4351         else if (joy & JOY_LEFT)
4352           result = 4;
4353       }
4354     }
4355
4356     if (level_solved)
4357     {
4358       if (global.use_envelope_request)
4359       {
4360         /* copy back current state of pressed buttons inside request area */
4361         BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4362       }
4363     }
4364
4365     BackToFront();
4366   }
4367
4368   return result;
4369 }
4370
4371 static boolean RequestDoor(char *text, unsigned int req_state)
4372 {
4373   unsigned int old_door_state;
4374   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4375   int font_nr = FONT_TEXT_2;
4376   char *text_ptr;
4377   int result;
4378   int ty;
4379
4380   if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4381   {
4382     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4383     font_nr = FONT_TEXT_1;
4384   }
4385
4386   if (game_status == GAME_MODE_PLAYING)
4387     BlitScreenToBitmap(backbuffer);
4388
4389   /* disable deactivated drawing when quick-loading level tape recording */
4390   if (tape.playing && tape.deactivate_display)
4391     TapeDeactivateDisplayOff(TRUE);
4392
4393   SetMouseCursor(CURSOR_DEFAULT);
4394
4395 #if defined(NETWORK_AVALIABLE)
4396   /* pause network game while waiting for request to answer */
4397   if (options.network &&
4398       game_status == GAME_MODE_PLAYING &&
4399       req_state & REQUEST_WAIT_FOR_INPUT)
4400     SendToServer_PausePlaying();
4401 #endif
4402
4403   old_door_state = GetDoorState();
4404
4405   /* simulate releasing mouse button over last gadget, if still pressed */
4406   if (button_status)
4407     HandleGadgets(-1, -1, 0);
4408
4409   UnmapAllGadgets();
4410
4411   /* draw released gadget before proceeding */
4412   // BackToFront();
4413
4414   if (old_door_state & DOOR_OPEN_1)
4415   {
4416     CloseDoor(DOOR_CLOSE_1);
4417
4418     /* save old door content */
4419     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4420                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4421   }
4422
4423   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4424   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4425
4426   /* clear door drawing field */
4427   DrawBackground(DX, DY, DXSIZE, DYSIZE);
4428
4429   /* force DOOR font inside door area */
4430   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4431
4432   /* write text for request */
4433   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4434   {
4435     char text_line[max_request_line_len + 1];
4436     int tx, tl, tc = 0;
4437
4438     if (!*text_ptr)
4439       break;
4440
4441     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4442     {
4443       tc = *(text_ptr + tx);
4444       // if (!tc || tc == ' ')
4445       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4446         break;
4447     }
4448
4449     if ((tc == '?' || tc == '!') && tl == 0)
4450       tl = 1;
4451
4452     if (!tl)
4453     { 
4454       text_ptr++; 
4455       ty--; 
4456       continue; 
4457     }
4458
4459     strncpy(text_line, text_ptr, tl);
4460     text_line[tl] = 0;
4461
4462     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4463              DY + 8 + ty * (getFontHeight(font_nr) + 2),
4464              text_line, font_nr);
4465
4466     text_ptr += tl + (tc == ' ' ? 1 : 0);
4467     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4468   }
4469
4470   ResetFontStatus();
4471
4472   if (req_state & REQ_ASK)
4473   {
4474     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4475     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4476   }
4477   else if (req_state & REQ_CONFIRM)
4478   {
4479     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4480   }
4481   else if (req_state & REQ_PLAYER)
4482   {
4483     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4484     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4485     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4486     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4487   }
4488
4489   /* copy request gadgets to door backbuffer */
4490   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4491
4492   OpenDoor(DOOR_OPEN_1);
4493
4494   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4495   {
4496     if (game_status == GAME_MODE_PLAYING)
4497     {
4498       SetPanelBackground();
4499       SetDrawBackgroundMask(REDRAW_DOOR_1);
4500     }
4501     else
4502     {
4503       SetDrawBackgroundMask(REDRAW_FIELD);
4504     }
4505
4506     return FALSE;
4507   }
4508
4509   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4510
4511   // ---------- handle request buttons ----------
4512   result = RequestHandleEvents(req_state);
4513
4514   UnmapToolButtons();
4515
4516   if (!(req_state & REQ_STAY_OPEN))
4517   {
4518     CloseDoor(DOOR_CLOSE_1);
4519
4520     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4521         (req_state & REQ_REOPEN))
4522       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4523   }
4524
4525   RemapAllGadgets();
4526
4527   if (game_status == GAME_MODE_PLAYING)
4528   {
4529     SetPanelBackground();
4530     SetDrawBackgroundMask(REDRAW_DOOR_1);
4531   }
4532   else
4533   {
4534     SetDrawBackgroundMask(REDRAW_FIELD);
4535   }
4536
4537 #if defined(NETWORK_AVALIABLE)
4538   /* continue network game after request */
4539   if (options.network &&
4540       game_status == GAME_MODE_PLAYING &&
4541       req_state & REQUEST_WAIT_FOR_INPUT)
4542     SendToServer_ContinuePlaying();
4543 #endif
4544
4545   /* restore deactivated drawing when quick-loading level tape recording */
4546   if (tape.playing && tape.deactivate_display)
4547     TapeDeactivateDisplayOn();
4548
4549   return result;
4550 }
4551
4552 static boolean RequestEnvelope(char *text, unsigned int req_state)
4553 {
4554   int result;
4555
4556   if (game_status == GAME_MODE_PLAYING)
4557     BlitScreenToBitmap(backbuffer);
4558
4559   /* disable deactivated drawing when quick-loading level tape recording */
4560   if (tape.playing && tape.deactivate_display)
4561     TapeDeactivateDisplayOff(TRUE);
4562
4563   SetMouseCursor(CURSOR_DEFAULT);
4564
4565 #if defined(NETWORK_AVALIABLE)
4566   /* pause network game while waiting for request to answer */
4567   if (options.network &&
4568       game_status == GAME_MODE_PLAYING &&
4569       req_state & REQUEST_WAIT_FOR_INPUT)
4570     SendToServer_PausePlaying();
4571 #endif
4572
4573   /* simulate releasing mouse button over last gadget, if still pressed */
4574   if (button_status)
4575     HandleGadgets(-1, -1, 0);
4576
4577   UnmapAllGadgets();
4578
4579   // (replace with setting corresponding request background)
4580   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4581   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4582
4583   /* clear door drawing field */
4584   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4585
4586   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4587
4588   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4589   {
4590     if (game_status == GAME_MODE_PLAYING)
4591     {
4592       SetPanelBackground();
4593       SetDrawBackgroundMask(REDRAW_DOOR_1);
4594     }
4595     else
4596     {
4597       SetDrawBackgroundMask(REDRAW_FIELD);
4598     }
4599
4600     return FALSE;
4601   }
4602
4603   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4604
4605   // ---------- handle request buttons ----------
4606   result = RequestHandleEvents(req_state);
4607
4608   UnmapToolButtons();
4609
4610   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4611
4612   RemapAllGadgets();
4613
4614   if (game_status == GAME_MODE_PLAYING)
4615   {
4616     SetPanelBackground();
4617     SetDrawBackgroundMask(REDRAW_DOOR_1);
4618   }
4619   else
4620   {
4621     SetDrawBackgroundMask(REDRAW_FIELD);
4622   }
4623
4624 #if defined(NETWORK_AVALIABLE)
4625   /* continue network game after request */
4626   if (options.network &&
4627       game_status == GAME_MODE_PLAYING &&
4628       req_state & REQUEST_WAIT_FOR_INPUT)
4629     SendToServer_ContinuePlaying();
4630 #endif
4631
4632   /* restore deactivated drawing when quick-loading level tape recording */
4633   if (tape.playing && tape.deactivate_display)
4634     TapeDeactivateDisplayOn();
4635
4636   return result;
4637 }
4638
4639 boolean Request(char *text, unsigned int req_state)
4640 {
4641   boolean overlay_active = GetOverlayActive();
4642   boolean result;
4643
4644   SetOverlayActive(FALSE);
4645
4646   if (global.use_envelope_request)
4647     result = RequestEnvelope(text, req_state);
4648   else
4649     result = RequestDoor(text, req_state);
4650
4651   SetOverlayActive(overlay_active);
4652
4653   return result;
4654 }
4655
4656 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4657 {
4658   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4659   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4660   int compare_result;
4661
4662   if (dpo1->sort_priority != dpo2->sort_priority)
4663     compare_result = dpo1->sort_priority - dpo2->sort_priority;
4664   else
4665     compare_result = dpo1->nr - dpo2->nr;
4666
4667   return compare_result;
4668 }
4669
4670 void InitGraphicCompatibilityInfo_Doors()
4671 {
4672   struct
4673   {
4674     int door_token;
4675     int part_1, part_8;
4676     struct DoorInfo *door;
4677   }
4678   doors[] =
4679   {
4680     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
4681     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
4682
4683     { -1,       -1,                     -1,                     NULL    }
4684   };
4685   struct Rect door_rect_list[] =
4686   {
4687     { DX, DY, DXSIZE, DYSIZE },
4688     { VX, VY, VXSIZE, VYSIZE }
4689   };
4690   int i, j;
4691
4692   for (i = 0; doors[i].door_token != -1; i++)
4693   {
4694     int door_token = doors[i].door_token;
4695     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4696     int part_1 = doors[i].part_1;
4697     int part_8 = doors[i].part_8;
4698     int part_2 = part_1 + 1;
4699     int part_3 = part_1 + 2;
4700     struct DoorInfo *door = doors[i].door;
4701     struct Rect *door_rect = &door_rect_list[door_index];
4702     boolean door_gfx_redefined = FALSE;
4703
4704     /* check if any door part graphic definitions have been redefined */
4705
4706     for (j = 0; door_part_controls[j].door_token != -1; j++)
4707     {
4708       struct DoorPartControlInfo *dpc = &door_part_controls[j];
4709       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4710
4711       if (dpc->door_token == door_token && fi->redefined)
4712         door_gfx_redefined = TRUE;
4713     }
4714
4715     /* check for old-style door graphic/animation modifications */
4716
4717     if (!door_gfx_redefined)
4718     {
4719       if (door->anim_mode & ANIM_STATIC_PANEL)
4720       {
4721         door->panel.step_xoffset = 0;
4722         door->panel.step_yoffset = 0;
4723       }
4724
4725       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4726       {
4727         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4728         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4729         int num_door_steps, num_panel_steps;
4730
4731         /* remove door part graphics other than the two default wings */
4732
4733         for (j = 0; door_part_controls[j].door_token != -1; j++)
4734         {
4735           struct DoorPartControlInfo *dpc = &door_part_controls[j];
4736           struct GraphicInfo *g = &graphic_info[dpc->graphic];
4737
4738           if (dpc->graphic >= part_3 &&
4739               dpc->graphic <= part_8)
4740             g->bitmap = NULL;
4741         }
4742
4743         /* set graphics and screen positions of the default wings */
4744
4745         g_part_1->width  = door_rect->width;
4746         g_part_1->height = door_rect->height;
4747         g_part_2->width  = door_rect->width;
4748         g_part_2->height = door_rect->height;
4749         g_part_2->src_x = door_rect->width;
4750         g_part_2->src_y = g_part_1->src_y;
4751
4752         door->part_2.x = door->part_1.x;
4753         door->part_2.y = door->part_1.y;
4754
4755         if (door->width != -1)
4756         {
4757           g_part_1->width = door->width;
4758           g_part_2->width = door->width;
4759
4760           // special treatment for graphics and screen position of right wing
4761           g_part_2->src_x += door_rect->width - door->width;
4762           door->part_2.x  += door_rect->width - door->width;
4763         }
4764
4765         if (door->height != -1)
4766         {
4767           g_part_1->height = door->height;
4768           g_part_2->height = door->height;
4769
4770           // special treatment for graphics and screen position of bottom wing
4771           g_part_2->src_y += door_rect->height - door->height;
4772           door->part_2.y  += door_rect->height - door->height;
4773         }
4774
4775         /* set animation delays for the default wings and panels */
4776
4777         door->part_1.step_delay = door->step_delay;
4778         door->part_2.step_delay = door->step_delay;
4779         door->panel.step_delay  = door->step_delay;
4780
4781         /* set animation draw order for the default wings */
4782
4783         door->part_1.sort_priority = 2; /* draw left wing over ... */
4784         door->part_2.sort_priority = 1; /*          ... right wing */
4785
4786         /* set animation draw offset for the default wings */
4787
4788         if (door->anim_mode & ANIM_HORIZONTAL)
4789         {
4790           door->part_1.step_xoffset = door->step_offset;
4791           door->part_1.step_yoffset = 0;
4792           door->part_2.step_xoffset = door->step_offset * -1;
4793           door->part_2.step_yoffset = 0;
4794
4795           num_door_steps = g_part_1->width / door->step_offset;
4796         }
4797         else    // ANIM_VERTICAL
4798         {
4799           door->part_1.step_xoffset = 0;
4800           door->part_1.step_yoffset = door->step_offset;
4801           door->part_2.step_xoffset = 0;
4802           door->part_2.step_yoffset = door->step_offset * -1;
4803
4804           num_door_steps = g_part_1->height / door->step_offset;
4805         }
4806
4807         /* set animation draw offset for the default panels */
4808
4809         if (door->step_offset > 1)
4810         {
4811           num_panel_steps = 2 * door_rect->height / door->step_offset;
4812           door->panel.start_step = num_panel_steps - num_door_steps;
4813           door->panel.start_step_closing = door->panel.start_step;
4814         }
4815         else
4816         {
4817           num_panel_steps = door_rect->height / door->step_offset;
4818           door->panel.start_step = num_panel_steps - num_door_steps / 2;
4819           door->panel.start_step_closing = door->panel.start_step;
4820           door->panel.step_delay *= 2;
4821         }
4822       }
4823     }
4824   }
4825 }
4826
4827 void InitDoors()
4828 {
4829   int i;
4830
4831   for (i = 0; door_part_controls[i].door_token != -1; i++)
4832   {
4833     struct DoorPartControlInfo *dpc = &door_part_controls[i];
4834     struct DoorPartOrderInfo *dpo = &door_part_order[i];
4835
4836     /* initialize "start_step_opening" and "start_step_closing", if needed */
4837     if (dpc->pos->start_step_opening == 0 &&
4838         dpc->pos->start_step_closing == 0)
4839     {
4840       // dpc->pos->start_step_opening = dpc->pos->start_step;
4841       dpc->pos->start_step_closing = dpc->pos->start_step;
4842     }
4843
4844     /* fill structure for door part draw order (sorted below) */
4845     dpo->nr = i;
4846     dpo->sort_priority = dpc->pos->sort_priority;
4847   }
4848
4849   /* sort door part controls according to sort_priority and graphic number */
4850   qsort(door_part_order, MAX_DOOR_PARTS,
4851         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4852 }
4853
4854 unsigned int OpenDoor(unsigned int door_state)
4855 {
4856   if (door_state & DOOR_COPY_BACK)
4857   {
4858     if (door_state & DOOR_OPEN_1)
4859       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4860                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4861
4862     if (door_state & DOOR_OPEN_2)
4863       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4864                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4865
4866     door_state &= ~DOOR_COPY_BACK;
4867   }
4868
4869   return MoveDoor(door_state);
4870 }
4871
4872 unsigned int CloseDoor(unsigned int door_state)
4873 {
4874   unsigned int old_door_state = GetDoorState();
4875
4876   if (!(door_state & DOOR_NO_COPY_BACK))
4877   {
4878     if (old_door_state & DOOR_OPEN_1)
4879       BlitBitmap(backbuffer, bitmap_db_door_1,
4880                  DX, DY, DXSIZE, DYSIZE, 0, 0);
4881
4882     if (old_door_state & DOOR_OPEN_2)
4883       BlitBitmap(backbuffer, bitmap_db_door_2,
4884                  VX, VY, VXSIZE, VYSIZE, 0, 0);
4885
4886     door_state &= ~DOOR_NO_COPY_BACK;
4887   }
4888
4889   return MoveDoor(door_state);
4890 }
4891
4892 unsigned int GetDoorState()
4893 {
4894   return MoveDoor(DOOR_GET_STATE);
4895 }
4896
4897 unsigned int SetDoorState(unsigned int door_state)
4898 {
4899   return MoveDoor(door_state | DOOR_SET_STATE);
4900 }
4901
4902 int euclid(int a, int b)
4903 {
4904   return (b ? euclid(b, a % b) : a);
4905 }
4906
4907 unsigned int MoveDoor(unsigned int door_state)
4908 {
4909   struct Rect door_rect_list[] =
4910   {
4911     { DX, DY, DXSIZE, DYSIZE },
4912     { VX, VY, VXSIZE, VYSIZE }
4913   };
4914   static int door1 = DOOR_CLOSE_1;
4915   static int door2 = DOOR_CLOSE_2;
4916   unsigned int door_delay = 0;
4917   unsigned int door_delay_value;
4918   int i;
4919
4920   if (door_state == DOOR_GET_STATE)
4921     return (door1 | door2);
4922
4923   if (door_state & DOOR_SET_STATE)
4924   {
4925     if (door_state & DOOR_ACTION_1)
4926       door1 = door_state & DOOR_ACTION_1;
4927     if (door_state & DOOR_ACTION_2)
4928       door2 = door_state & DOOR_ACTION_2;
4929
4930     return (door1 | door2);
4931   }
4932
4933   if (!(door_state & DOOR_FORCE_REDRAW))
4934   {
4935     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4936       door_state &= ~DOOR_OPEN_1;
4937     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4938       door_state &= ~DOOR_CLOSE_1;
4939     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4940       door_state &= ~DOOR_OPEN_2;
4941     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4942       door_state &= ~DOOR_CLOSE_2;
4943   }
4944
4945   if (global.autoplay_leveldir)
4946   {
4947     door_state |= DOOR_NO_DELAY;
4948     door_state &= ~DOOR_CLOSE_ALL;
4949   }
4950
4951   if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
4952     door_state |= DOOR_NO_DELAY;
4953
4954   if (door_state & DOOR_ACTION)
4955   {
4956     boolean door_panel_drawn[NUM_DOORS];
4957     boolean panel_has_doors[NUM_DOORS];
4958     boolean door_part_skip[MAX_DOOR_PARTS];
4959     boolean door_part_done[MAX_DOOR_PARTS];
4960     boolean door_part_done_all;
4961     int num_steps[MAX_DOOR_PARTS];
4962     int max_move_delay = 0;     // delay for complete animations of all doors
4963     int max_step_delay = 0;     // delay (ms) between two animation frames
4964     int num_move_steps = 0;     // number of animation steps for all doors
4965     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
4966     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
4967     int current_move_delay = 0;
4968     int start = 0;
4969     int k;
4970
4971     for (i = 0; i < NUM_DOORS; i++)
4972       panel_has_doors[i] = FALSE;
4973
4974     for (i = 0; i < MAX_DOOR_PARTS; i++)
4975     {
4976       struct DoorPartControlInfo *dpc = &door_part_controls[i];
4977       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4978       int door_token = dpc->door_token;
4979
4980       door_part_done[i] = FALSE;
4981       door_part_skip[i] = (!(door_state & door_token) ||
4982                            !g->bitmap);
4983     }
4984
4985     for (i = 0; i < MAX_DOOR_PARTS; i++)
4986     {
4987       int nr = door_part_order[i].nr;
4988       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4989       struct DoorPartPosInfo *pos = dpc->pos;
4990       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4991       int door_token = dpc->door_token;
4992       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4993       boolean is_panel = DOOR_PART_IS_PANEL(nr);
4994       int step_xoffset = ABS(pos->step_xoffset);
4995       int step_yoffset = ABS(pos->step_yoffset);
4996       int step_delay = pos->step_delay;
4997       int current_door_state = door_state & door_token;
4998       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4999       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5000       boolean part_opening = (is_panel ? door_closing : door_opening);
5001       int start_step = (part_opening ? pos->start_step_opening :
5002                         pos->start_step_closing);
5003       float move_xsize = (step_xoffset ? g->width  : 0);
5004       float move_ysize = (step_yoffset ? g->height : 0);
5005       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5006       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5007       int move_steps = (move_xsteps && move_ysteps ?
5008                         MIN(move_xsteps, move_ysteps) :
5009                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
5010       int move_delay = move_steps * step_delay;
5011
5012       if (door_part_skip[nr])
5013         continue;
5014
5015       max_move_delay = MAX(max_move_delay, move_delay);
5016       max_step_delay = (max_step_delay == 0 ? step_delay :
5017                         euclid(max_step_delay, step_delay));
5018       num_steps[nr] = move_steps;
5019
5020       if (!is_panel)
5021       {
5022         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5023
5024         panel_has_doors[door_index] = TRUE;
5025       }
5026     }
5027
5028     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
5029
5030     num_move_steps = max_move_delay / max_step_delay;
5031     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5032
5033     door_delay_value = max_step_delay;
5034
5035     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5036     {
5037       start = num_move_steps - 1;
5038     }
5039     else
5040     {
5041       /* opening door sound has priority over simultaneously closing door */
5042       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5043       {
5044         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5045
5046         if (door_state & DOOR_OPEN_1)
5047           PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5048         if (door_state & DOOR_OPEN_2)
5049           PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5050       }
5051       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5052       {
5053         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5054
5055         if (door_state & DOOR_CLOSE_1)
5056           PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5057         if (door_state & DOOR_CLOSE_2)
5058           PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5059       }
5060     }
5061
5062     for (k = start; k < num_move_steps; k++)
5063     {
5064       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
5065
5066       door_part_done_all = TRUE;
5067
5068       for (i = 0; i < NUM_DOORS; i++)
5069         door_panel_drawn[i] = FALSE;
5070
5071       for (i = 0; i < MAX_DOOR_PARTS; i++)
5072       {
5073         int nr = door_part_order[i].nr;
5074         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5075         struct DoorPartPosInfo *pos = dpc->pos;
5076         struct GraphicInfo *g = &graphic_info[dpc->graphic];
5077         int door_token = dpc->door_token;
5078         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5079         boolean is_panel = DOOR_PART_IS_PANEL(nr);
5080         boolean is_panel_and_door_has_closed = FALSE;
5081         struct Rect *door_rect = &door_rect_list[door_index];
5082         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5083                                   bitmap_db_door_2);
5084         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5085         int current_door_state = door_state & door_token;
5086         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5087         boolean door_closing = !door_opening;
5088         boolean part_opening = (is_panel ? door_closing : door_opening);
5089         boolean part_closing = !part_opening;
5090         int start_step = (part_opening ? pos->start_step_opening :
5091                           pos->start_step_closing);
5092         int step_delay = pos->step_delay;
5093         int step_factor = step_delay / max_step_delay;
5094         int k1 = (step_factor ? k / step_factor + 1 : k);
5095         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5096         int kk = MAX(0, k2);
5097         int g_src_x = 0;
5098         int g_src_y = 0;
5099         int src_x, src_y, src_xx, src_yy;
5100         int dst_x, dst_y, dst_xx, dst_yy;
5101         int width, height;
5102
5103         if (door_part_skip[nr])
5104           continue;
5105
5106         if (!(door_state & door_token))
5107           continue;
5108
5109         if (!g->bitmap)
5110           continue;
5111
5112         if (!is_panel)
5113         {
5114           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5115           int kk_door = MAX(0, k2_door);
5116           int sync_frame = kk_door * door_delay_value;
5117           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5118
5119           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5120                                 &g_src_x, &g_src_y);
5121         }
5122
5123         // draw door panel
5124
5125         if (!door_panel_drawn[door_index])
5126         {
5127           ClearRectangle(drawto, door_rect->x, door_rect->y,
5128                          door_rect->width, door_rect->height);
5129
5130           door_panel_drawn[door_index] = TRUE;
5131         }
5132
5133         // draw opening or closing door parts
5134
5135         if (pos->step_xoffset < 0)      // door part on right side
5136         {
5137           src_xx = 0;
5138           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5139           width = g->width;
5140
5141           if (dst_xx + width > door_rect->width)
5142             width = door_rect->width - dst_xx;
5143         }
5144         else                            // door part on left side
5145         {
5146           src_xx = 0;
5147           dst_xx = pos->x - kk * pos->step_xoffset;
5148
5149           if (dst_xx < 0)
5150           {
5151             src_xx = ABS(dst_xx);
5152             dst_xx = 0;
5153           }
5154
5155           width = g->width - src_xx;
5156
5157           if (width > door_rect->width)
5158             width = door_rect->width;
5159
5160           // printf("::: k == %d [%d] \n", k, start_step);
5161         }
5162
5163         if (pos->step_yoffset < 0)      // door part on bottom side
5164         {
5165           src_yy = 0;
5166           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5167           height = g->height;
5168
5169           if (dst_yy + height > door_rect->height)
5170             height = door_rect->height - dst_yy;
5171         }
5172         else                            // door part on top side
5173         {
5174           src_yy = 0;
5175           dst_yy = pos->y - kk * pos->step_yoffset;
5176
5177           if (dst_yy < 0)
5178           {
5179             src_yy = ABS(dst_yy);
5180             dst_yy = 0;
5181           }
5182
5183           height = g->height - src_yy;
5184         }
5185
5186         src_x = g_src_x + src_xx;
5187         src_y = g_src_y + src_yy;
5188
5189         dst_x = door_rect->x + dst_xx;
5190         dst_y = door_rect->y + dst_yy;
5191
5192         is_panel_and_door_has_closed =
5193           (is_panel &&
5194            door_closing &&
5195            panel_has_doors[door_index] &&
5196            k >= num_move_steps_doors_only - 1);
5197
5198         if (width  >= 0 && width  <= g->width &&
5199             height >= 0 && height <= g->height &&
5200             !is_panel_and_door_has_closed)
5201         {
5202           if (is_panel || !pos->draw_masked)
5203             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5204                        dst_x, dst_y);
5205           else
5206             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5207                              dst_x, dst_y);
5208         }
5209
5210         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5211
5212         if ((part_opening && (width < 0         || height < 0)) ||
5213             (part_closing && (width >= g->width && height >= g->height)))
5214           door_part_done[nr] = TRUE;
5215
5216         // continue door part animations, but not panel after door has closed
5217         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5218           door_part_done_all = FALSE;
5219       }
5220
5221       if (!(door_state & DOOR_NO_DELAY))
5222       {
5223         BackToFront();
5224
5225         SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5226
5227         current_move_delay += max_step_delay;
5228
5229         /* prevent OS (Windows) from complaining about program not responding */
5230         CheckQuitEvent();
5231       }
5232
5233       if (door_part_done_all)
5234         break;
5235     }
5236
5237     if (!(door_state & DOOR_NO_DELAY))
5238     {
5239       /* wait for specified door action post delay */
5240       if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5241         door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5242       else if (door_state & DOOR_ACTION_1)
5243         door_delay_value = door_1.post_delay;
5244       else if (door_state & DOOR_ACTION_2)
5245         door_delay_value = door_2.post_delay;
5246
5247       while (!DelayReached(&door_delay, door_delay_value))
5248         BackToFront();
5249     }
5250   }
5251
5252   if (door_state & DOOR_ACTION_1)
5253     door1 = door_state & DOOR_ACTION_1;
5254   if (door_state & DOOR_ACTION_2)
5255     door2 = door_state & DOOR_ACTION_2;
5256
5257   // draw masked border over door area
5258   DrawMaskedBorder(REDRAW_DOOR_1);
5259   DrawMaskedBorder(REDRAW_DOOR_2);
5260
5261   return (door1 | door2);
5262 }
5263
5264 static boolean useSpecialEditorDoor()
5265 {
5266   int graphic = IMG_GLOBAL_BORDER_EDITOR;
5267   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5268
5269   // do not draw special editor door if editor border defined or redefined
5270   if (graphic_info[graphic].bitmap != NULL || redefined)
5271     return FALSE;
5272
5273   // do not draw special editor door if global border defined to be empty
5274   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5275     return FALSE;
5276
5277   // do not draw special editor door if viewport definitions do not match
5278   if (EX != VX ||
5279       EY >= VY ||
5280       EXSIZE != VXSIZE ||
5281       EY + EYSIZE != VY + VYSIZE)
5282     return FALSE;
5283
5284   return TRUE;
5285 }
5286
5287 void DrawSpecialEditorDoor()
5288 {
5289   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5290   int top_border_width = gfx1->width;
5291   int top_border_height = gfx1->height;
5292   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5293   int ex = EX - outer_border;
5294   int ey = EY - outer_border;
5295   int vy = VY - outer_border;
5296   int exsize = EXSIZE + 2 * outer_border;
5297
5298   if (!useSpecialEditorDoor())
5299     return;
5300
5301   /* draw bigger level editor toolbox window */
5302   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5303              top_border_width, top_border_height, ex, ey - top_border_height);
5304   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5305              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5306
5307   redraw_mask |= REDRAW_ALL;
5308 }
5309
5310 void UndrawSpecialEditorDoor()
5311 {
5312   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5313   int top_border_width = gfx1->width;
5314   int top_border_height = gfx1->height;
5315   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5316   int ex = EX - outer_border;
5317   int ey = EY - outer_border;
5318   int ey_top = ey - top_border_height;
5319   int exsize = EXSIZE + 2 * outer_border;
5320   int eysize = EYSIZE + 2 * outer_border;
5321
5322   if (!useSpecialEditorDoor())
5323     return;
5324
5325   /* draw normal tape recorder window */
5326   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5327   {
5328     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5329                ex, ey_top, top_border_width, top_border_height,
5330                ex, ey_top);
5331     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5332                ex, ey, exsize, eysize, ex, ey);
5333   }
5334   else
5335   {
5336     // if screen background is set to "[NONE]", clear editor toolbox window
5337     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5338     ClearRectangle(drawto, ex, ey, exsize, eysize);
5339   }
5340
5341   redraw_mask |= REDRAW_ALL;
5342 }
5343
5344
5345 /* ---------- new tool button stuff ---------------------------------------- */
5346
5347 static struct
5348 {
5349   int graphic;
5350   struct TextPosInfo *pos;
5351   int gadget_id;
5352   char *infotext;
5353 } toolbutton_info[NUM_TOOL_BUTTONS] =
5354 {
5355   {
5356     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
5357     TOOL_CTRL_ID_YES,                   "yes"
5358   },
5359   {
5360     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5361     TOOL_CTRL_ID_NO,                    "no"
5362   },
5363   {
5364     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5365     TOOL_CTRL_ID_CONFIRM,               "confirm"
5366   },
5367   {
5368     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5369     TOOL_CTRL_ID_PLAYER_1,              "player 1"
5370   },
5371   {
5372     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5373     TOOL_CTRL_ID_PLAYER_2,              "player 2"
5374   },
5375   {
5376     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5377     TOOL_CTRL_ID_PLAYER_3,              "player 3"
5378   },
5379   {
5380     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5381     TOOL_CTRL_ID_PLAYER_4,              "player 4"
5382   }
5383 };
5384
5385 void CreateToolButtons()
5386 {
5387   int i;
5388
5389   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5390   {
5391     int graphic = toolbutton_info[i].graphic;
5392     struct GraphicInfo *gfx = &graphic_info[graphic];
5393     struct TextPosInfo *pos = toolbutton_info[i].pos;
5394     struct GadgetInfo *gi;
5395     Bitmap *deco_bitmap = None;
5396     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5397     unsigned int event_mask = GD_EVENT_RELEASED;
5398     int dx = DX;
5399     int dy = DY;
5400     int gd_x = gfx->src_x;
5401     int gd_y = gfx->src_y;
5402     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5403     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5404     int x = pos->x;
5405     int y = pos->y;
5406     int id = i;
5407
5408     if (global.use_envelope_request)
5409     {
5410       setRequestPosition(&dx, &dy, TRUE);
5411
5412       // check if request buttons are outside of envelope and fix, if needed
5413       if (x < 0 || x + gfx->width  > request.width ||
5414           y < 0 || y + gfx->height > request.height)
5415       {
5416         if (id == TOOL_CTRL_ID_YES)
5417         {
5418           x = 0;
5419           y = request.height - 2 * request.border_size - gfx->height;
5420         }
5421         else if (id == TOOL_CTRL_ID_NO)
5422         {
5423           x = request.width  - 2 * request.border_size - gfx->width;
5424           y = request.height - 2 * request.border_size - gfx->height;
5425         }
5426         else if (id == TOOL_CTRL_ID_CONFIRM)
5427         {
5428           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5429           y = request.height - 2 * request.border_size - gfx->height;
5430         }
5431         else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5432         {
5433           int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5434
5435           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5436           y = request.height - 2 * request.border_size - gfx->height * 2;
5437
5438           x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5439           y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5440         }
5441       }
5442     }
5443
5444     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5445     {
5446       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5447
5448       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5449                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5450       deco_xpos = (gfx->width  - pos->size) / 2;
5451       deco_ypos = (gfx->height - pos->size) / 2;
5452     }
5453
5454     gi = CreateGadget(GDI_CUSTOM_ID, id,
5455                       GDI_IMAGE_ID, graphic,
5456                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5457                       GDI_X, dx + x,
5458                       GDI_Y, dy + y,
5459                       GDI_WIDTH, gfx->width,
5460                       GDI_HEIGHT, gfx->height,
5461                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5462                       GDI_STATE, GD_BUTTON_UNPRESSED,
5463                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5464                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5465                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5466                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5467                       GDI_DECORATION_SIZE, pos->size, pos->size,
5468                       GDI_DECORATION_SHIFTING, 1, 1,
5469                       GDI_DIRECT_DRAW, FALSE,
5470                       GDI_EVENT_MASK, event_mask,
5471                       GDI_CALLBACK_ACTION, HandleToolButtons,
5472                       GDI_END);
5473
5474     if (gi == NULL)
5475       Error(ERR_EXIT, "cannot create gadget");
5476
5477     tool_gadget[id] = gi;
5478   }
5479 }
5480
5481 void FreeToolButtons()
5482 {
5483   int i;
5484
5485   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5486     FreeGadget(tool_gadget[i]);
5487 }
5488
5489 static void UnmapToolButtons()
5490 {
5491   int i;
5492
5493   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5494     UnmapGadget(tool_gadget[i]);
5495 }
5496
5497 static void HandleToolButtons(struct GadgetInfo *gi)
5498 {
5499   request_gadget_id = gi->custom_id;
5500 }
5501
5502 static struct Mapping_EM_to_RND_object
5503 {
5504   int element_em;
5505   boolean is_rnd_to_em_mapping;         /* unique mapping EM <-> RND */
5506   boolean is_backside;                  /* backside of moving element */
5507
5508   int element_rnd;
5509   int action;
5510   int direction;
5511 }
5512 em_object_mapping_list[] =
5513 {
5514   {
5515     Xblank,                             TRUE,   FALSE,
5516     EL_EMPTY,                           -1, -1
5517   },
5518   {
5519     Yacid_splash_eB,                    FALSE,  FALSE,
5520     EL_ACID_SPLASH_RIGHT,               -1, -1
5521   },
5522   {
5523     Yacid_splash_wB,                    FALSE,  FALSE,
5524     EL_ACID_SPLASH_LEFT,                -1, -1
5525   },
5526
5527 #ifdef EM_ENGINE_BAD_ROLL
5528   {
5529     Xstone_force_e,                     FALSE,  FALSE,
5530     EL_ROCK,                            -1, MV_BIT_RIGHT
5531   },
5532   {
5533     Xstone_force_w,                     FALSE,  FALSE,
5534     EL_ROCK,                            -1, MV_BIT_LEFT
5535   },
5536   {
5537     Xnut_force_e,                       FALSE,  FALSE,
5538     EL_NUT,                             -1, MV_BIT_RIGHT
5539   },
5540   {
5541     Xnut_force_w,                       FALSE,  FALSE,
5542     EL_NUT,                             -1, MV_BIT_LEFT
5543   },
5544   {
5545     Xspring_force_e,                    FALSE,  FALSE,
5546     EL_SPRING,                          -1, MV_BIT_RIGHT
5547   },
5548   {
5549     Xspring_force_w,                    FALSE,  FALSE,
5550     EL_SPRING,                          -1, MV_BIT_LEFT
5551   },
5552   {
5553     Xemerald_force_e,                   FALSE,  FALSE,
5554     EL_EMERALD,                         -1, MV_BIT_RIGHT
5555   },
5556   {
5557     Xemerald_force_w,                   FALSE,  FALSE,
5558     EL_EMERALD,                         -1, MV_BIT_LEFT
5559   },
5560   {
5561     Xdiamond_force_e,                   FALSE,  FALSE,
5562     EL_DIAMOND,                         -1, MV_BIT_RIGHT
5563   },
5564   {
5565     Xdiamond_force_w,                   FALSE,  FALSE,
5566     EL_DIAMOND,                         -1, MV_BIT_LEFT
5567   },
5568   {
5569     Xbomb_force_e,                      FALSE,  FALSE,
5570     EL_BOMB,                            -1, MV_BIT_RIGHT
5571   },
5572   {
5573     Xbomb_force_w,                      FALSE,  FALSE,
5574     EL_BOMB,                            -1, MV_BIT_LEFT
5575   },
5576 #endif  /* EM_ENGINE_BAD_ROLL */
5577
5578   {
5579     Xstone,                             TRUE,   FALSE,
5580     EL_ROCK,                            -1, -1
5581   },
5582   {
5583     Xstone_pause,                       FALSE,  FALSE,
5584     EL_ROCK,                            -1, -1
5585   },
5586   {
5587     Xstone_fall,                        FALSE,  FALSE,
5588     EL_ROCK,                            -1, -1
5589   },
5590   {
5591     Ystone_s,                           FALSE,  FALSE,
5592     EL_ROCK,                            ACTION_FALLING, -1
5593   },
5594   {
5595     Ystone_sB,                          FALSE,  TRUE,
5596     EL_ROCK,                            ACTION_FALLING, -1
5597   },
5598   {
5599     Ystone_e,                           FALSE,  FALSE,
5600     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5601   },
5602   {
5603     Ystone_eB,                          FALSE,  TRUE,
5604     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5605   },
5606   {
5607     Ystone_w,                           FALSE,  FALSE,
5608     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5609   },
5610   {
5611     Ystone_wB,                          FALSE,  TRUE,
5612     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5613   },
5614   {
5615     Xnut,                               TRUE,   FALSE,
5616     EL_NUT,                             -1, -1
5617   },
5618   {
5619     Xnut_pause,                         FALSE,  FALSE,
5620     EL_NUT,                             -1, -1
5621   },
5622   {
5623     Xnut_fall,                          FALSE,  FALSE,
5624     EL_NUT,                             -1, -1
5625   },
5626   {
5627     Ynut_s,                             FALSE,  FALSE,
5628     EL_NUT,                             ACTION_FALLING, -1
5629   },
5630   {
5631     Ynut_sB,                            FALSE,  TRUE,
5632     EL_NUT,                             ACTION_FALLING, -1
5633   },
5634   {
5635     Ynut_e,                             FALSE,  FALSE,
5636     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5637   },
5638   {
5639     Ynut_eB,                            FALSE,  TRUE,
5640     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5641   },
5642   {
5643     Ynut_w,                             FALSE,  FALSE,
5644     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5645   },
5646   {
5647     Ynut_wB,                            FALSE,  TRUE,
5648     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5649   },
5650   {
5651     Xbug_n,                             TRUE,   FALSE,
5652     EL_BUG_UP,                          -1, -1
5653   },
5654   {
5655     Xbug_e,                             TRUE,   FALSE,
5656     EL_BUG_RIGHT,                       -1, -1
5657   },
5658   {
5659     Xbug_s,                             TRUE,   FALSE,
5660     EL_BUG_DOWN,                        -1, -1
5661   },
5662   {
5663     Xbug_w,                             TRUE,   FALSE,
5664     EL_BUG_LEFT,                        -1, -1
5665   },
5666   {
5667     Xbug_gon,                           FALSE,  FALSE,
5668     EL_BUG_UP,                          -1, -1
5669   },
5670   {
5671     Xbug_goe,                           FALSE,  FALSE,
5672     EL_BUG_RIGHT,                       -1, -1
5673   },
5674   {
5675     Xbug_gos,                           FALSE,  FALSE,
5676     EL_BUG_DOWN,                        -1, -1
5677   },
5678   {
5679     Xbug_gow,                           FALSE,  FALSE,
5680     EL_BUG_LEFT,                        -1, -1
5681   },
5682   {
5683     Ybug_n,                             FALSE,  FALSE,
5684     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5685   },
5686   {
5687     Ybug_nB,                            FALSE,  TRUE,
5688     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5689   },
5690   {
5691     Ybug_e,                             FALSE,  FALSE,
5692     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5693   },
5694   {
5695     Ybug_eB,                            FALSE,  TRUE,
5696     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5697   },
5698   {
5699     Ybug_s,                             FALSE,  FALSE,
5700     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5701   },
5702   {
5703     Ybug_sB,                            FALSE,  TRUE,
5704     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5705   },
5706   {
5707     Ybug_w,                             FALSE,  FALSE,
5708     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5709   },
5710   {
5711     Ybug_wB,                            FALSE,  TRUE,
5712     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5713   },
5714   {
5715     Ybug_w_n,                           FALSE,  FALSE,
5716     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5717   },
5718   {
5719     Ybug_n_e,                           FALSE,  FALSE,
5720     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5721   },
5722   {
5723     Ybug_e_s,                           FALSE,  FALSE,
5724     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5725   },
5726   {
5727     Ybug_s_w,                           FALSE,  FALSE,
5728     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5729   },
5730   {
5731     Ybug_e_n,                           FALSE,  FALSE,
5732     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5733   },
5734   {
5735     Ybug_s_e,                           FALSE,  FALSE,
5736     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5737   },
5738   {
5739     Ybug_w_s,                           FALSE,  FALSE,
5740     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5741   },
5742   {
5743     Ybug_n_w,                           FALSE,  FALSE,
5744     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5745   },
5746   {
5747     Ybug_stone,                         FALSE,  FALSE,
5748     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
5749   },
5750   {
5751     Ybug_spring,                        FALSE,  FALSE,
5752     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
5753   },
5754   {
5755     Xtank_n,                            TRUE,   FALSE,
5756     EL_SPACESHIP_UP,                    -1, -1
5757   },
5758   {
5759     Xtank_e,                            TRUE,   FALSE,
5760     EL_SPACESHIP_RIGHT,                 -1, -1
5761   },
5762   {
5763     Xtank_s,                            TRUE,   FALSE,
5764     EL_SPACESHIP_DOWN,                  -1, -1
5765   },
5766   {
5767     Xtank_w,                            TRUE,   FALSE,
5768     EL_SPACESHIP_LEFT,                  -1, -1
5769   },
5770   {
5771     Xtank_gon,                          FALSE,  FALSE,
5772     EL_SPACESHIP_UP,                    -1, -1
5773   },
5774   {
5775     Xtank_goe,                          FALSE,  FALSE,
5776     EL_SPACESHIP_RIGHT,                 -1, -1
5777   },
5778   {
5779     Xtank_gos,                          FALSE,  FALSE,
5780     EL_SPACESHIP_DOWN,                  -1, -1
5781   },
5782   {
5783     Xtank_gow,                          FALSE,  FALSE,
5784     EL_SPACESHIP_LEFT,                  -1, -1
5785   },
5786   {
5787     Ytank_n,                            FALSE,  FALSE,
5788     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5789   },
5790   {
5791     Ytank_nB,                           FALSE,  TRUE,
5792     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5793   },
5794   {
5795     Ytank_e,                            FALSE,  FALSE,
5796     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5797   },
5798   {
5799     Ytank_eB,                           FALSE,  TRUE,
5800     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5801   },
5802   {
5803     Ytank_s,                            FALSE,  FALSE,
5804     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5805   },
5806   {
5807     Ytank_sB,                           FALSE,  TRUE,
5808     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5809   },
5810   {
5811     Ytank_w,                            FALSE,  FALSE,
5812     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5813   },
5814   {
5815     Ytank_wB,                           FALSE,  TRUE,
5816     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5817   },
5818   {
5819     Ytank_w_n,                          FALSE,  FALSE,
5820     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5821   },
5822   {
5823     Ytank_n_e,                          FALSE,  FALSE,
5824     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5825   },
5826   {
5827     Ytank_e_s,                          FALSE,  FALSE,
5828     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5829   },
5830   {
5831     Ytank_s_w,                          FALSE,  FALSE,
5832     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5833   },
5834   {
5835     Ytank_e_n,                          FALSE,  FALSE,
5836     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5837   },
5838   {
5839     Ytank_s_e,                          FALSE,  FALSE,
5840     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5841   },
5842   {
5843     Ytank_w_s,                          FALSE,  FALSE,
5844     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5845   },
5846   {
5847     Ytank_n_w,                          FALSE,  FALSE,
5848     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5849   },
5850   {
5851     Ytank_stone,                        FALSE,  FALSE,
5852     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
5853   },
5854   {
5855     Ytank_spring,                       FALSE,  FALSE,
5856     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
5857   },
5858   {
5859     Xandroid,                           TRUE,   FALSE,
5860     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
5861   },
5862   {
5863     Xandroid_1_n,                       FALSE,  FALSE,
5864     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5865   },
5866   {
5867     Xandroid_2_n,                       FALSE,  FALSE,
5868     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5869   },
5870   {
5871     Xandroid_1_e,                       FALSE,  FALSE,
5872     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5873   },
5874   {
5875     Xandroid_2_e,                       FALSE,  FALSE,
5876     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5877   },
5878   {
5879     Xandroid_1_w,                       FALSE,  FALSE,
5880     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5881   },
5882   {
5883     Xandroid_2_w,                       FALSE,  FALSE,
5884     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5885   },
5886   {
5887     Xandroid_1_s,                       FALSE,  FALSE,
5888     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5889   },
5890   {
5891     Xandroid_2_s,                       FALSE,  FALSE,
5892     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5893   },
5894   {
5895     Yandroid_n,                         FALSE,  FALSE,
5896     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5897   },
5898   {
5899     Yandroid_nB,                        FALSE,  TRUE,
5900     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5901   },
5902   {
5903     Yandroid_ne,                        FALSE,  FALSE,
5904     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
5905   },
5906   {
5907     Yandroid_neB,                       FALSE,  TRUE,
5908     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
5909   },
5910   {
5911     Yandroid_e,                         FALSE,  FALSE,
5912     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5913   },
5914   {
5915     Yandroid_eB,                        FALSE,  TRUE,
5916     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5917   },
5918   {
5919     Yandroid_se,                        FALSE,  FALSE,
5920     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
5921   },
5922   {
5923     Yandroid_seB,                       FALSE,  TRUE,
5924     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5925   },
5926   {
5927     Yandroid_s,                         FALSE,  FALSE,
5928     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5929   },
5930   {
5931     Yandroid_sB,                        FALSE,  TRUE,
5932     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5933   },
5934   {
5935     Yandroid_sw,                        FALSE,  FALSE,
5936     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
5937   },
5938   {
5939     Yandroid_swB,                       FALSE,  TRUE,
5940     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
5941   },
5942   {
5943     Yandroid_w,                         FALSE,  FALSE,
5944     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5945   },
5946   {
5947     Yandroid_wB,                        FALSE,  TRUE,
5948     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5949   },
5950   {
5951     Yandroid_nw,                        FALSE,  FALSE,
5952     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
5953   },
5954   {
5955     Yandroid_nwB,                       FALSE,  TRUE,
5956     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
5957   },
5958   {
5959     Xspring,                            TRUE,   FALSE,
5960     EL_SPRING,                          -1, -1
5961   },
5962   {
5963     Xspring_pause,                      FALSE,  FALSE,
5964     EL_SPRING,                          -1, -1
5965   },
5966   {
5967     Xspring_e,                          FALSE,  FALSE,
5968     EL_SPRING,                          -1, -1
5969   },
5970   {
5971     Xspring_w,                          FALSE,  FALSE,
5972     EL_SPRING,                          -1, -1
5973   },
5974   {
5975     Xspring_fall,                       FALSE,  FALSE,
5976     EL_SPRING,                          -1, -1
5977   },
5978   {
5979     Yspring_s,                          FALSE,  FALSE,
5980     EL_SPRING,                          ACTION_FALLING, -1
5981   },
5982   {
5983     Yspring_sB,                         FALSE,  TRUE,
5984     EL_SPRING,                          ACTION_FALLING, -1
5985   },
5986   {
5987     Yspring_e,                          FALSE,  FALSE,
5988     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5989   },
5990   {
5991     Yspring_eB,                         FALSE,  TRUE,
5992     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5993   },
5994   {
5995     Yspring_w,                          FALSE,  FALSE,
5996     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5997   },
5998   {
5999     Yspring_wB,                         FALSE,  TRUE,
6000     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6001   },
6002   {
6003     Yspring_kill_e,                     FALSE,  FALSE,
6004     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6005   },
6006   {
6007     Yspring_kill_eB,                    FALSE,  TRUE,
6008     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6009   },
6010   {
6011     Yspring_kill_w,                     FALSE,  FALSE,
6012     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6013   },
6014   {
6015     Yspring_kill_wB,                    FALSE,  TRUE,
6016     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6017   },
6018   {
6019     Xeater_n,                           TRUE,   FALSE,
6020     EL_YAMYAM_UP,                       -1, -1
6021   },
6022   {
6023     Xeater_e,                           TRUE,   FALSE,
6024     EL_YAMYAM_RIGHT,                    -1, -1
6025   },
6026   {
6027     Xeater_w,                           TRUE,   FALSE,
6028     EL_YAMYAM_LEFT,                     -1, -1
6029   },
6030   {
6031     Xeater_s,                           TRUE,   FALSE,
6032     EL_YAMYAM_DOWN,                     -1, -1
6033   },
6034   {
6035     Yeater_n,                           FALSE,  FALSE,
6036     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6037   },
6038   {
6039     Yeater_nB,                          FALSE,  TRUE,
6040     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6041   },
6042   {
6043     Yeater_e,                           FALSE,  FALSE,
6044     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6045   },
6046   {
6047     Yeater_eB,                          FALSE,  TRUE,
6048     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6049   },
6050   {
6051     Yeater_s,                           FALSE,  FALSE,
6052     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6053   },
6054   {
6055     Yeater_sB,                          FALSE,  TRUE,
6056     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6057   },
6058   {
6059     Yeater_w,                           FALSE,  FALSE,
6060     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6061   },
6062   {
6063     Yeater_wB,                          FALSE,  TRUE,
6064     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6065   },
6066   {
6067     Yeater_stone,                       FALSE,  FALSE,
6068     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
6069   },
6070   {
6071     Yeater_spring,                      FALSE,  FALSE,
6072     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
6073   },
6074   {
6075     Xalien,                             TRUE,   FALSE,
6076     EL_ROBOT,                           -1, -1
6077   },
6078   {
6079     Xalien_pause,                       FALSE,  FALSE,
6080     EL_ROBOT,                           -1, -1
6081   },
6082   {
6083     Yalien_n,                           FALSE,  FALSE,
6084     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6085   },
6086   {
6087     Yalien_nB,                          FALSE,  TRUE,
6088     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6089   },
6090   {
6091     Yalien_e,                           FALSE,  FALSE,
6092     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6093   },
6094   {
6095     Yalien_eB,                          FALSE,  TRUE,
6096     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6097   },
6098   {
6099     Yalien_s,                           FALSE,  FALSE,
6100     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6101   },
6102   {
6103     Yalien_sB,                          FALSE,  TRUE,
6104     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6105   },
6106   {
6107     Yalien_w,                           FALSE,  FALSE,
6108     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6109   },
6110   {
6111     Yalien_wB,                          FALSE,  TRUE,
6112     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6113   },
6114   {
6115     Yalien_stone,                       FALSE,  FALSE,
6116     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
6117   },
6118   {
6119     Yalien_spring,                      FALSE,  FALSE,
6120     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
6121   },
6122   {
6123     Xemerald,                           TRUE,   FALSE,
6124     EL_EMERALD,                         -1, -1
6125   },
6126   {
6127     Xemerald_pause,                     FALSE,  FALSE,
6128     EL_EMERALD,                         -1, -1
6129   },
6130   {
6131     Xemerald_fall,                      FALSE,  FALSE,
6132     EL_EMERALD,                         -1, -1
6133   },
6134   {
6135     Xemerald_shine,                     FALSE,  FALSE,
6136     EL_EMERALD,                         ACTION_TWINKLING, -1
6137   },
6138   {
6139     Yemerald_s,                         FALSE,  FALSE,
6140     EL_EMERALD,                         ACTION_FALLING, -1
6141   },
6142   {
6143     Yemerald_sB,                        FALSE,  TRUE,
6144     EL_EMERALD,                         ACTION_FALLING, -1
6145   },
6146   {
6147     Yemerald_e,                         FALSE,  FALSE,
6148     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6149   },
6150   {
6151     Yemerald_eB,                        FALSE,  TRUE,
6152     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6153   },
6154   {
6155     Yemerald_w,                         FALSE,  FALSE,
6156     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6157   },
6158   {
6159     Yemerald_wB,                        FALSE,  TRUE,
6160     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6161   },
6162   {
6163     Yemerald_eat,                       FALSE,  FALSE,
6164     EL_EMERALD,                         ACTION_COLLECTING, -1
6165   },
6166   {
6167     Yemerald_stone,                     FALSE,  FALSE,
6168     EL_NUT,                             ACTION_BREAKING, -1
6169   },
6170   {
6171     Xdiamond,                           TRUE,   FALSE,
6172     EL_DIAMOND,                         -1, -1
6173   },
6174   {
6175     Xdiamond_pause,                     FALSE,  FALSE,
6176     EL_DIAMOND,                         -1, -1
6177   },
6178   {
6179     Xdiamond_fall,                      FALSE,  FALSE,
6180     EL_DIAMOND,                         -1, -1
6181   },
6182   {
6183     Xdiamond_shine,                     FALSE,  FALSE,
6184     EL_DIAMOND,                         ACTION_TWINKLING, -1
6185   },
6186   {
6187     Ydiamond_s,                         FALSE,  FALSE,
6188     EL_DIAMOND,                         ACTION_FALLING, -1
6189   },
6190   {
6191     Ydiamond_sB,                        FALSE,  TRUE,
6192     EL_DIAMOND,                         ACTION_FALLING, -1
6193   },
6194   {
6195     Ydiamond_e,                         FALSE,  FALSE,
6196     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6197   },
6198   {
6199     Ydiamond_eB,                        FALSE,  TRUE,
6200     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6201   },
6202   {
6203     Ydiamond_w,                         FALSE,  FALSE,
6204     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6205   },
6206   {
6207     Ydiamond_wB,                        FALSE,  TRUE,
6208     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6209   },
6210   {
6211     Ydiamond_eat,                       FALSE,  FALSE,
6212     EL_DIAMOND,                         ACTION_COLLECTING, -1
6213   },
6214   {
6215     Ydiamond_stone,                     FALSE,  FALSE,
6216     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
6217   },
6218   {
6219     Xdrip_fall,                         TRUE,   FALSE,
6220     EL_AMOEBA_DROP,                     -1, -1
6221   },
6222   {
6223     Xdrip_stretch,                      FALSE,  FALSE,
6224     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6225   },
6226   {
6227     Xdrip_stretchB,                     FALSE,  TRUE,
6228     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6229   },
6230   {
6231     Xdrip_eat,                          FALSE,  FALSE,
6232     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
6233   },
6234   {
6235     Ydrip_s1,                           FALSE,  FALSE,
6236     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6237   },
6238   {
6239     Ydrip_s1B,                          FALSE,  TRUE,
6240     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6241   },
6242   {
6243     Ydrip_s2,                           FALSE,  FALSE,
6244     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6245   },
6246   {
6247     Ydrip_s2B,                          FALSE,  TRUE,
6248     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6249   },
6250   {
6251     Xbomb,                              TRUE,   FALSE,
6252     EL_BOMB,                            -1, -1
6253   },
6254   {
6255     Xbomb_pause,                        FALSE,  FALSE,
6256     EL_BOMB,                            -1, -1
6257   },
6258   {
6259     Xbomb_fall,                         FALSE,  FALSE,
6260     EL_BOMB,                            -1, -1
6261   },
6262   {
6263     Ybomb_s,                            FALSE,  FALSE,
6264     EL_BOMB,                            ACTION_FALLING, -1
6265   },
6266   {
6267     Ybomb_sB,                           FALSE,  TRUE,
6268     EL_BOMB,                            ACTION_FALLING, -1
6269   },
6270   {
6271     Ybomb_e,                            FALSE,  FALSE,
6272     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6273   },
6274   {
6275     Ybomb_eB,                           FALSE,  TRUE,
6276     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6277   },
6278   {
6279     Ybomb_w,                            FALSE,  FALSE,
6280     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6281   },
6282   {
6283     Ybomb_wB,                           FALSE,  TRUE,
6284     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6285   },
6286   {
6287     Ybomb_eat,                          FALSE,  FALSE,
6288     EL_BOMB,                            ACTION_ACTIVATING, -1
6289   },
6290   {
6291     Xballoon,                           TRUE,   FALSE,
6292     EL_BALLOON,                         -1, -1
6293   },
6294   {
6295     Yballoon_n,                         FALSE,  FALSE,
6296     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6297   },
6298   {
6299     Yballoon_nB,                        FALSE,  TRUE,
6300     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6301   },
6302   {
6303     Yballoon_e,                         FALSE,  FALSE,
6304     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6305   },
6306   {
6307     Yballoon_eB,                        FALSE,  TRUE,
6308     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6309   },
6310   {
6311     Yballoon_s,                         FALSE,  FALSE,
6312     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6313   },
6314   {
6315     Yballoon_sB,                        FALSE,  TRUE,
6316     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6317   },
6318   {
6319     Yballoon_w,                         FALSE,  FALSE,
6320     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6321   },
6322   {
6323     Yballoon_wB,                        FALSE,  TRUE,
6324     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6325   },
6326   {
6327     Xgrass,                             TRUE,   FALSE,
6328     EL_EMC_GRASS,                       -1, -1
6329   },
6330   {
6331     Ygrass_nB,                          FALSE,  FALSE,
6332     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
6333   },
6334   {
6335     Ygrass_eB,                          FALSE,  FALSE,
6336     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
6337   },
6338   {
6339     Ygrass_sB,                          FALSE,  FALSE,
6340     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
6341   },
6342   {
6343     Ygrass_wB,                          FALSE,  FALSE,
6344     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
6345   },
6346   {
6347     Xdirt,                              TRUE,   FALSE,
6348     EL_SAND,                            -1, -1
6349   },
6350   {
6351     Ydirt_nB,                           FALSE,  FALSE,
6352     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
6353   },
6354   {
6355     Ydirt_eB,                           FALSE,  FALSE,
6356     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
6357   },
6358   {
6359     Ydirt_sB,                           FALSE,  FALSE,
6360     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
6361   },
6362   {
6363     Ydirt_wB,                           FALSE,  FALSE,
6364     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
6365   },
6366   {
6367     Xacid_ne,                           TRUE,   FALSE,
6368     EL_ACID_POOL_TOPRIGHT,              -1, -1
6369   },
6370   {
6371     Xacid_se,                           TRUE,   FALSE,
6372     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
6373   },
6374   {
6375     Xacid_s,                            TRUE,   FALSE,
6376     EL_ACID_POOL_BOTTOM,                -1, -1
6377   },
6378   {
6379     Xacid_sw,                           TRUE,   FALSE,
6380     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
6381   },
6382   {
6383     Xacid_nw,                           TRUE,   FALSE,
6384     EL_ACID_POOL_TOPLEFT,               -1, -1
6385   },
6386   {
6387     Xacid_1,                            TRUE,   FALSE,
6388     EL_ACID,                            -1, -1
6389   },
6390   {
6391     Xacid_2,                            FALSE,  FALSE,
6392     EL_ACID,                            -1, -1
6393   },
6394   {
6395     Xacid_3,                            FALSE,  FALSE,
6396     EL_ACID,                            -1, -1
6397   },
6398   {
6399     Xacid_4,                            FALSE,  FALSE,
6400     EL_ACID,                            -1, -1
6401   },
6402   {
6403     Xacid_5,                            FALSE,  FALSE,
6404     EL_ACID,                            -1, -1
6405   },
6406   {
6407     Xacid_6,                            FALSE,  FALSE,
6408     EL_ACID,                            -1, -1
6409   },
6410   {
6411     Xacid_7,                            FALSE,  FALSE,
6412     EL_ACID,                            -1, -1
6413   },
6414   {
6415     Xacid_8,                            FALSE,  FALSE,
6416     EL_ACID,                            -1, -1
6417   },
6418   {
6419     Xball_1,                            TRUE,   FALSE,
6420     EL_EMC_MAGIC_BALL,                  -1, -1
6421   },
6422   {
6423     Xball_1B,                           FALSE,  FALSE,
6424     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6425   },
6426   {
6427     Xball_2,                            FALSE,  FALSE,
6428     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6429   },
6430   {
6431     Xball_2B,                           FALSE,  FALSE,
6432     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6433   },
6434   {
6435     Yball_eat,                          FALSE,  FALSE,
6436     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
6437   },
6438   {
6439     Ykey_1_eat,                         FALSE,  FALSE,
6440     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
6441   },
6442   {
6443     Ykey_2_eat,                         FALSE,  FALSE,
6444     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
6445   },
6446   {
6447     Ykey_3_eat,                         FALSE,  FALSE,
6448     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
6449   },
6450   {
6451     Ykey_4_eat,                         FALSE,  FALSE,
6452     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
6453   },
6454   {
6455     Ykey_5_eat,                         FALSE,  FALSE,
6456     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
6457   },
6458   {
6459     Ykey_6_eat,                         FALSE,  FALSE,
6460     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
6461   },
6462   {
6463     Ykey_7_eat,                         FALSE,  FALSE,
6464     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
6465   },
6466   {
6467     Ykey_8_eat,                         FALSE,  FALSE,
6468     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
6469   },
6470   {
6471     Ylenses_eat,                        FALSE,  FALSE,
6472     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
6473   },
6474   {
6475     Ymagnify_eat,                       FALSE,  FALSE,
6476     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
6477   },
6478   {
6479     Ygrass_eat,                         FALSE,  FALSE,
6480     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
6481   },
6482   {
6483     Ydirt_eat,                          FALSE,  FALSE,
6484     EL_SAND,                            ACTION_SNAPPING, -1
6485   },
6486   {
6487     Xgrow_ns,                           TRUE,   FALSE,
6488     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
6489   },
6490   {
6491     Ygrow_ns_eat,                       FALSE,  FALSE,
6492     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
6493   },
6494   {
6495     Xgrow_ew,                           TRUE,   FALSE,
6496     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
6497   },
6498   {
6499     Ygrow_ew_eat,                       FALSE,  FALSE,
6500     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
6501   },
6502   {
6503     Xwonderwall,                        TRUE,   FALSE,
6504     EL_MAGIC_WALL,                      -1, -1
6505   },
6506   {
6507     XwonderwallB,                       FALSE,  FALSE,
6508     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
6509   },
6510   {
6511     Xamoeba_1,                          TRUE,   FALSE,
6512     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6513   },
6514   {
6515     Xamoeba_2,                          FALSE,  FALSE,
6516     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6517   },
6518   {
6519     Xamoeba_3,                          FALSE,  FALSE,
6520     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6521   },
6522   {
6523     Xamoeba_4,                          FALSE,  FALSE,
6524     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6525   },
6526   {
6527     Xamoeba_5,                          TRUE,   FALSE,
6528     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6529   },
6530   {
6531     Xamoeba_6,                          FALSE,  FALSE,
6532     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6533   },
6534   {
6535     Xamoeba_7,                          FALSE,  FALSE,
6536     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6537   },
6538   {
6539     Xamoeba_8,                          FALSE,  FALSE,
6540     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6541   },
6542   {
6543     Xdoor_1,                            TRUE,   FALSE,
6544     EL_EM_GATE_1,                       -1, -1
6545   },
6546   {
6547     Xdoor_2,                            TRUE,   FALSE,
6548     EL_EM_GATE_2,                       -1, -1
6549   },
6550   {
6551     Xdoor_3,                            TRUE,   FALSE,
6552     EL_EM_GATE_3,                       -1, -1
6553   },
6554   {
6555     Xdoor_4,                            TRUE,   FALSE,
6556     EL_EM_GATE_4,                       -1, -1
6557   },
6558   {
6559     Xdoor_5,                            TRUE,   FALSE,
6560     EL_EMC_GATE_5,                      -1, -1
6561   },
6562   {
6563     Xdoor_6,                            TRUE,   FALSE,
6564     EL_EMC_GATE_6,                      -1, -1
6565   },
6566   {
6567     Xdoor_7,                            TRUE,   FALSE,
6568     EL_EMC_GATE_7,                      -1, -1
6569   },
6570   {
6571     Xdoor_8,                            TRUE,   FALSE,
6572     EL_EMC_GATE_8,                      -1, -1
6573   },
6574   {
6575     Xkey_1,                             TRUE,   FALSE,
6576     EL_EM_KEY_1,                        -1, -1
6577   },
6578   {
6579     Xkey_2,                             TRUE,   FALSE,
6580     EL_EM_KEY_2,                        -1, -1
6581   },
6582   {
6583     Xkey_3,                             TRUE,   FALSE,
6584     EL_EM_KEY_3,                        -1, -1
6585   },
6586   {
6587     Xkey_4,                             TRUE,   FALSE,
6588     EL_EM_KEY_4,                        -1, -1
6589   },
6590   {
6591     Xkey_5,                             TRUE,   FALSE,
6592     EL_EMC_KEY_5,                       -1, -1
6593   },
6594   {
6595     Xkey_6,                             TRUE,   FALSE,
6596     EL_EMC_KEY_6,                       -1, -1
6597   },
6598   {
6599     Xkey_7,                             TRUE,   FALSE,
6600     EL_EMC_KEY_7,                       -1, -1
6601   },
6602   {
6603     Xkey_8,                             TRUE,   FALSE,
6604     EL_EMC_KEY_8,                       -1, -1
6605   },
6606   {
6607     Xwind_n,                            TRUE,   FALSE,
6608     EL_BALLOON_SWITCH_UP,               -1, -1
6609   },
6610   {
6611     Xwind_e,                            TRUE,   FALSE,
6612     EL_BALLOON_SWITCH_RIGHT,            -1, -1
6613   },
6614   {
6615     Xwind_s,                            TRUE,   FALSE,
6616     EL_BALLOON_SWITCH_DOWN,             -1, -1
6617   },
6618   {
6619     Xwind_w,                            TRUE,   FALSE,
6620     EL_BALLOON_SWITCH_LEFT,             -1, -1
6621   },
6622   {
6623     Xwind_nesw,                         TRUE,   FALSE,
6624     EL_BALLOON_SWITCH_ANY,              -1, -1
6625   },
6626   {
6627     Xwind_stop,                         TRUE,   FALSE,
6628     EL_BALLOON_SWITCH_NONE,             -1, -1
6629   },
6630   {
6631     Xexit,                              TRUE,   FALSE,
6632     EL_EM_EXIT_CLOSED,                  -1, -1
6633   },
6634   {
6635     Xexit_1,                            TRUE,   FALSE,
6636     EL_EM_EXIT_OPEN,                    -1, -1
6637   },
6638   {
6639     Xexit_2,                            FALSE,  FALSE,
6640     EL_EM_EXIT_OPEN,                    -1, -1
6641   },
6642   {
6643     Xexit_3,                            FALSE,  FALSE,
6644     EL_EM_EXIT_OPEN,                    -1, -1
6645   },
6646   {
6647     Xdynamite,                          TRUE,   FALSE,
6648     EL_EM_DYNAMITE,                     -1, -1
6649   },
6650   {
6651     Ydynamite_eat,                      FALSE,  FALSE,
6652     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6653   },
6654   {
6655     Xdynamite_1,                        TRUE,   FALSE,
6656     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6657   },
6658   {
6659     Xdynamite_2,                        FALSE,  FALSE,
6660     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6661   },
6662   {
6663     Xdynamite_3,                        FALSE,  FALSE,
6664     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6665   },
6666   {
6667     Xdynamite_4,                        FALSE,  FALSE,
6668     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6669   },
6670   {
6671     Xbumper,                            TRUE,   FALSE,
6672     EL_EMC_SPRING_BUMPER,               -1, -1
6673   },
6674   {
6675     XbumperB,                           FALSE,  FALSE,
6676     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
6677   },
6678   {
6679     Xwheel,                             TRUE,   FALSE,
6680     EL_ROBOT_WHEEL,                     -1, -1
6681   },
6682   {
6683     XwheelB,                            FALSE,  FALSE,
6684     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
6685   },
6686   {
6687     Xswitch,                            TRUE,   FALSE,
6688     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
6689   },
6690   {
6691     XswitchB,                           FALSE,  FALSE,
6692     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
6693   },
6694   {
6695     Xsand,                              TRUE,   FALSE,
6696     EL_QUICKSAND_EMPTY,                 -1, -1
6697   },
6698   {
6699     Xsand_stone,                        TRUE,   FALSE,
6700     EL_QUICKSAND_FULL,                  -1, -1
6701   },
6702   {
6703     Xsand_stonein_1,                    FALSE,  TRUE,
6704     EL_ROCK,                            ACTION_FILLING, -1
6705   },
6706   {
6707     Xsand_stonein_2,                    FALSE,  TRUE,
6708     EL_ROCK,                            ACTION_FILLING, -1
6709   },
6710   {
6711     Xsand_stonein_3,                    FALSE,  TRUE,
6712     EL_ROCK,                            ACTION_FILLING, -1
6713   },
6714   {
6715     Xsand_stonein_4,                    FALSE,  TRUE,
6716     EL_ROCK,                            ACTION_FILLING, -1
6717   },
6718   {
6719     Xsand_stonesand_1,                  FALSE,  FALSE,
6720     EL_QUICKSAND_EMPTYING,              -1, -1
6721   },
6722   {
6723     Xsand_stonesand_2,                  FALSE,  FALSE,
6724     EL_QUICKSAND_EMPTYING,              -1, -1
6725   },
6726   {
6727     Xsand_stonesand_3,                  FALSE,  FALSE,
6728     EL_QUICKSAND_EMPTYING,              -1, -1
6729   },
6730   {
6731     Xsand_stonesand_4,                  FALSE,  FALSE,
6732     EL_QUICKSAND_EMPTYING,              -1, -1
6733   },
6734   {
6735     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
6736     EL_QUICKSAND_EMPTYING,              -1, -1
6737   },
6738   {
6739     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
6740     EL_QUICKSAND_EMPTYING,              -1, -1
6741   },
6742   {
6743     Xsand_stoneout_1,                   FALSE,  FALSE,
6744     EL_ROCK,                            ACTION_EMPTYING, -1
6745   },
6746   {
6747     Xsand_stoneout_2,                   FALSE,  FALSE,
6748     EL_ROCK,                            ACTION_EMPTYING, -1
6749   },
6750   {
6751     Xsand_sandstone_1,                  FALSE,  FALSE,
6752     EL_QUICKSAND_FILLING,               -1, -1
6753   },
6754   {
6755     Xsand_sandstone_2,                  FALSE,  FALSE,
6756     EL_QUICKSAND_FILLING,               -1, -1
6757   },
6758   {
6759     Xsand_sandstone_3,                  FALSE,  FALSE,
6760     EL_QUICKSAND_FILLING,               -1, -1
6761   },
6762   {
6763     Xsand_sandstone_4,                  FALSE,  FALSE,
6764     EL_QUICKSAND_FILLING,               -1, -1
6765   },
6766   {
6767     Xplant,                             TRUE,   FALSE,
6768     EL_EMC_PLANT,                       -1, -1
6769   },
6770   {
6771     Yplant,                             FALSE,  FALSE,
6772     EL_EMC_PLANT,                       -1, -1
6773   },
6774   {
6775     Xlenses,                            TRUE,   FALSE,
6776     EL_EMC_LENSES,                      -1, -1
6777   },
6778   {
6779     Xmagnify,                           TRUE,   FALSE,
6780     EL_EMC_MAGNIFIER,                   -1, -1
6781   },
6782   {
6783     Xdripper,                           TRUE,   FALSE,
6784     EL_EMC_DRIPPER,                     -1, -1
6785   },
6786   {
6787     XdripperB,                          FALSE,  FALSE,
6788     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
6789   },
6790   {
6791     Xfake_blank,                        TRUE,   FALSE,
6792     EL_INVISIBLE_WALL,                  -1, -1
6793   },
6794   {
6795     Xfake_blankB,                       FALSE,  FALSE,
6796     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
6797   },
6798   {
6799     Xfake_grass,                        TRUE,   FALSE,
6800     EL_EMC_FAKE_GRASS,                  -1, -1
6801   },
6802   {
6803     Xfake_grassB,                       FALSE,  FALSE,
6804     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
6805   },
6806   {
6807     Xfake_door_1,                       TRUE,   FALSE,
6808     EL_EM_GATE_1_GRAY,                  -1, -1
6809   },
6810   {
6811     Xfake_door_2,                       TRUE,   FALSE,
6812     EL_EM_GATE_2_GRAY,                  -1, -1
6813   },
6814   {
6815     Xfake_door_3,                       TRUE,   FALSE,
6816     EL_EM_GATE_3_GRAY,                  -1, -1
6817   },
6818   {
6819     Xfake_door_4,                       TRUE,   FALSE,
6820     EL_EM_GATE_4_GRAY,                  -1, -1
6821   },
6822   {
6823     Xfake_door_5,                       TRUE,   FALSE,
6824     EL_EMC_GATE_5_GRAY,                 -1, -1
6825   },
6826   {
6827     Xfake_door_6,                       TRUE,   FALSE,
6828     EL_EMC_GATE_6_GRAY,                 -1, -1
6829   },
6830   {
6831     Xfake_door_7,                       TRUE,   FALSE,
6832     EL_EMC_GATE_7_GRAY,                 -1, -1
6833   },
6834   {
6835     Xfake_door_8,                       TRUE,   FALSE,
6836     EL_EMC_GATE_8_GRAY,                 -1, -1
6837   },
6838   {
6839     Xfake_acid_1,                       TRUE,   FALSE,
6840     EL_EMC_FAKE_ACID,                   -1, -1
6841   },
6842   {
6843     Xfake_acid_2,                       FALSE,  FALSE,
6844     EL_EMC_FAKE_ACID,                   -1, -1
6845   },
6846   {
6847     Xfake_acid_3,                       FALSE,  FALSE,
6848     EL_EMC_FAKE_ACID,                   -1, -1
6849   },
6850   {
6851     Xfake_acid_4,                       FALSE,  FALSE,
6852     EL_EMC_FAKE_ACID,                   -1, -1
6853   },
6854   {
6855     Xfake_acid_5,                       FALSE,  FALSE,
6856     EL_EMC_FAKE_ACID,                   -1, -1
6857   },
6858   {
6859     Xfake_acid_6,                       FALSE,  FALSE,
6860     EL_EMC_FAKE_ACID,                   -1, -1
6861   },
6862   {
6863     Xfake_acid_7,                       FALSE,  FALSE,
6864     EL_EMC_FAKE_ACID,                   -1, -1
6865   },
6866   {
6867     Xfake_acid_8,                       FALSE,  FALSE,
6868     EL_EMC_FAKE_ACID,                   -1, -1
6869   },
6870   {
6871     Xsteel_1,                           TRUE,   FALSE,
6872     EL_STEELWALL,                       -1, -1
6873   },
6874   {
6875     Xsteel_2,                           TRUE,   FALSE,
6876     EL_EMC_STEELWALL_2,                 -1, -1
6877   },
6878   {
6879     Xsteel_3,                           TRUE,   FALSE,
6880     EL_EMC_STEELWALL_3,                 -1, -1
6881   },
6882   {
6883     Xsteel_4,                           TRUE,   FALSE,
6884     EL_EMC_STEELWALL_4,                 -1, -1
6885   },
6886   {
6887     Xwall_1,                            TRUE,   FALSE,
6888     EL_WALL,                            -1, -1
6889   },
6890   {
6891     Xwall_2,                            TRUE,   FALSE,
6892     EL_EMC_WALL_14,                     -1, -1
6893   },
6894   {
6895     Xwall_3,                            TRUE,   FALSE,
6896     EL_EMC_WALL_15,                     -1, -1
6897   },
6898   {
6899     Xwall_4,                            TRUE,   FALSE,
6900     EL_EMC_WALL_16,                     -1, -1
6901   },
6902   {
6903     Xround_wall_1,                      TRUE,   FALSE,
6904     EL_WALL_SLIPPERY,                   -1, -1
6905   },
6906   {
6907     Xround_wall_2,                      TRUE,   FALSE,
6908     EL_EMC_WALL_SLIPPERY_2,             -1, -1
6909   },
6910   {
6911     Xround_wall_3,                      TRUE,   FALSE,
6912     EL_EMC_WALL_SLIPPERY_3,             -1, -1
6913   },
6914   {
6915     Xround_wall_4,                      TRUE,   FALSE,
6916     EL_EMC_WALL_SLIPPERY_4,             -1, -1
6917   },
6918   {
6919     Xdecor_1,                           TRUE,   FALSE,
6920     EL_EMC_WALL_8,                      -1, -1
6921   },
6922   {
6923     Xdecor_2,                           TRUE,   FALSE,
6924     EL_EMC_WALL_6,                      -1, -1
6925   },
6926   {
6927     Xdecor_3,                           TRUE,   FALSE,
6928     EL_EMC_WALL_4,                      -1, -1
6929   },
6930   {
6931     Xdecor_4,                           TRUE,   FALSE,
6932     EL_EMC_WALL_7,                      -1, -1
6933   },
6934   {
6935     Xdecor_5,                           TRUE,   FALSE,
6936     EL_EMC_WALL_5,                      -1, -1
6937   },
6938   {
6939     Xdecor_6,                           TRUE,   FALSE,
6940     EL_EMC_WALL_9,                      -1, -1
6941   },
6942   {
6943     Xdecor_7,                           TRUE,   FALSE,
6944     EL_EMC_WALL_10,                     -1, -1
6945   },
6946   {
6947     Xdecor_8,                           TRUE,   FALSE,
6948     EL_EMC_WALL_1,                      -1, -1
6949   },
6950   {
6951     Xdecor_9,                           TRUE,   FALSE,
6952     EL_EMC_WALL_2,                      -1, -1
6953   },
6954   {
6955     Xdecor_10,                          TRUE,   FALSE,
6956     EL_EMC_WALL_3,                      -1, -1
6957   },
6958   {
6959     Xdecor_11,                          TRUE,   FALSE,
6960     EL_EMC_WALL_11,                     -1, -1
6961   },
6962   {
6963     Xdecor_12,                          TRUE,   FALSE,
6964     EL_EMC_WALL_12,                     -1, -1
6965   },
6966   {
6967     Xalpha_0,                           TRUE,   FALSE,
6968     EL_CHAR('0'),                       -1, -1
6969   },
6970   {
6971     Xalpha_1,                           TRUE,   FALSE,
6972     EL_CHAR('1'),                       -1, -1
6973   },
6974   {
6975     Xalpha_2,                           TRUE,   FALSE,
6976     EL_CHAR('2'),                       -1, -1
6977   },
6978   {
6979     Xalpha_3,                           TRUE,   FALSE,
6980     EL_CHAR('3'),                       -1, -1
6981   },
6982   {
6983     Xalpha_4,                           TRUE,   FALSE,
6984     EL_CHAR('4'),                       -1, -1
6985   },
6986   {
6987     Xalpha_5,                           TRUE,   FALSE,
6988     EL_CHAR('5'),                       -1, -1
6989   },
6990   {
6991     Xalpha_6,                           TRUE,   FALSE,
6992     EL_CHAR('6'),                       -1, -1
6993   },
6994   {
6995     Xalpha_7,                           TRUE,   FALSE,
6996     EL_CHAR('7'),                       -1, -1
6997   },
6998   {
6999     Xalpha_8,                           TRUE,   FALSE,
7000     EL_CHAR('8'),                       -1, -1
7001   },
7002   {
7003     Xalpha_9,                           TRUE,   FALSE,
7004     EL_CHAR('9'),                       -1, -1
7005   },
7006   {
7007     Xalpha_excla,                       TRUE,   FALSE,
7008     EL_CHAR('!'),                       -1, -1
7009   },
7010   {
7011     Xalpha_quote,                       TRUE,   FALSE,
7012     EL_CHAR('"'),                       -1, -1
7013   },
7014   {
7015     Xalpha_comma,                       TRUE,   FALSE,
7016     EL_CHAR(','),                       -1, -1
7017   },
7018   {
7019     Xalpha_minus,                       TRUE,   FALSE,
7020     EL_CHAR('-'),                       -1, -1
7021   },
7022   {
7023     Xalpha_perio,                       TRUE,   FALSE,
7024     EL_CHAR('.'),                       -1, -1
7025   },
7026   {
7027     Xalpha_colon,                       TRUE,   FALSE,
7028     EL_CHAR(':'),                       -1, -1
7029   },
7030   {
7031     Xalpha_quest,                       TRUE,   FALSE,
7032     EL_CHAR('?'),                       -1, -1
7033   },
7034   {
7035     Xalpha_a,                           TRUE,   FALSE,
7036     EL_CHAR('A'),                       -1, -1
7037   },
7038   {
7039     Xalpha_b,                           TRUE,   FALSE,
7040     EL_CHAR('B'),                       -1, -1
7041   },
7042   {
7043     Xalpha_c,                           TRUE,   FALSE,
7044     EL_CHAR('C'),                       -1, -1
7045   },
7046   {
7047     Xalpha_d,                           TRUE,   FALSE,
7048     EL_CHAR('D'),                       -1, -1
7049   },
7050   {
7051     Xalpha_e,                           TRUE,   FALSE,
7052     EL_CHAR('E'),                       -1, -1
7053   },
7054   {
7055     Xalpha_f,                           TRUE,   FALSE,
7056     EL_CHAR('F'),                       -1, -1
7057   },
7058   {
7059     Xalpha_g,                           TRUE,   FALSE,
7060     EL_CHAR('G'),                       -1, -1
7061   },
7062   {
7063     Xalpha_h,                           TRUE,   FALSE,
7064     EL_CHAR('H'),                       -1, -1
7065   },
7066   {
7067     Xalpha_i,                           TRUE,   FALSE,
7068     EL_CHAR('I'),                       -1, -1
7069   },
7070   {
7071     Xalpha_j,                           TRUE,   FALSE,
7072     EL_CHAR('J'),                       -1, -1
7073   },
7074   {
7075     Xalpha_k,                           TRUE,   FALSE,
7076     EL_CHAR('K'),                       -1, -1
7077   },
7078   {
7079     Xalpha_l,                           TRUE,   FALSE,
7080     EL_CHAR('L'),                       -1, -1
7081   },
7082   {
7083     Xalpha_m,                           TRUE,   FALSE,
7084     EL_CHAR('M'),                       -1, -1
7085   },
7086   {
7087     Xalpha_n,                           TRUE,   FALSE,
7088     EL_CHAR('N'),                       -1, -1
7089   },
7090   {
7091     Xalpha_o,                           TRUE,   FALSE,
7092     EL_CHAR('O'),                       -1, -1
7093   },
7094   {
7095     Xalpha_p,                           TRUE,   FALSE,
7096     EL_CHAR('P'),                       -1, -1
7097   },
7098   {
7099     Xalpha_q,                           TRUE,   FALSE,
7100     EL_CHAR('Q'),                       -1, -1
7101   },
7102   {
7103     Xalpha_r,                           TRUE,   FALSE,
7104     EL_CHAR('R'),                       -1, -1
7105   },
7106   {
7107     Xalpha_s,                           TRUE,   FALSE,
7108     EL_CHAR('S'),                       -1, -1
7109   },
7110   {
7111     Xalpha_t,                           TRUE,   FALSE,
7112     EL_CHAR('T'),                       -1, -1
7113   },
7114   {
7115     Xalpha_u,                           TRUE,   FALSE,
7116     EL_CHAR('U'),                       -1, -1
7117   },
7118   {
7119     Xalpha_v,                           TRUE,   FALSE,
7120     EL_CHAR('V'),                       -1, -1
7121   },
7122   {
7123     Xalpha_w,                           TRUE,   FALSE,
7124     EL_CHAR('W'),                       -1, -1
7125   },
7126   {
7127     Xalpha_x,                           TRUE,   FALSE,
7128     EL_CHAR('X'),                       -1, -1
7129   },
7130   {
7131     Xalpha_y,                           TRUE,   FALSE,
7132     EL_CHAR('Y'),                       -1, -1
7133   },
7134   {
7135     Xalpha_z,                           TRUE,   FALSE,
7136     EL_CHAR('Z'),                       -1, -1
7137   },
7138   {
7139     Xalpha_arrow_e,                     TRUE,   FALSE,
7140     EL_CHAR('>'),                       -1, -1
7141   },
7142   {
7143     Xalpha_arrow_w,                     TRUE,   FALSE,
7144     EL_CHAR('<'),                       -1, -1
7145   },
7146   {
7147     Xalpha_copyr,                       TRUE,   FALSE,
7148     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
7149   },
7150
7151   {
7152     Xboom_bug,                          FALSE,  FALSE,
7153     EL_BUG,                             ACTION_EXPLODING, -1
7154   },
7155   {
7156     Xboom_bomb,                         FALSE,  FALSE,
7157     EL_BOMB,                            ACTION_EXPLODING, -1
7158   },
7159   {
7160     Xboom_android,                      FALSE,  FALSE,
7161     EL_EMC_ANDROID,                     ACTION_OTHER, -1
7162   },
7163   {
7164     Xboom_1,                            FALSE,  FALSE,
7165     EL_DEFAULT,                         ACTION_EXPLODING, -1
7166   },
7167   {
7168     Xboom_2,                            FALSE,  FALSE,
7169     EL_DEFAULT,                         ACTION_EXPLODING, -1
7170   },
7171   {
7172     Znormal,                            FALSE,  FALSE,
7173     EL_EMPTY,                           -1, -1
7174   },
7175   {
7176     Zdynamite,                          FALSE,  FALSE,
7177     EL_EMPTY,                           -1, -1
7178   },
7179   {
7180     Zplayer,                            FALSE,  FALSE,
7181     EL_EMPTY,                           -1, -1
7182   },
7183   {
7184     ZBORDER,                            FALSE,  FALSE,
7185     EL_EMPTY,                           -1, -1
7186   },
7187
7188   {
7189     -1,                                 FALSE,  FALSE,
7190     -1,                                 -1, -1
7191   }
7192 };
7193
7194 static struct Mapping_EM_to_RND_player
7195 {
7196   int action_em;
7197   int player_nr;
7198
7199   int element_rnd;
7200   int action;
7201   int direction;
7202 }
7203 em_player_mapping_list[] =
7204 {
7205   {
7206     SPR_walk + 0,                       0,
7207     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
7208   },
7209   {
7210     SPR_walk + 1,                       0,
7211     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
7212   },
7213   {
7214     SPR_walk + 2,                       0,
7215     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
7216   },
7217   {
7218     SPR_walk + 3,                       0,
7219     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
7220   },
7221   {
7222     SPR_push + 0,                       0,
7223     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
7224   },
7225   {
7226     SPR_push + 1,                       0,
7227     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
7228   },
7229   {
7230     SPR_push + 2,                       0,
7231     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
7232   },
7233   {
7234     SPR_push + 3,                       0,
7235     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
7236   },
7237   {
7238     SPR_spray + 0,                      0,
7239     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
7240   },
7241   {
7242     SPR_spray + 1,                      0,
7243     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7244   },
7245   {
7246     SPR_spray + 2,                      0,
7247     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
7248   },
7249   {
7250     SPR_spray + 3,                      0,
7251     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
7252   },
7253   {
7254     SPR_walk + 0,                       1,
7255     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
7256   },
7257   {
7258     SPR_walk + 1,                       1,
7259     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
7260   },
7261   {
7262     SPR_walk + 2,                       1,
7263     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
7264   },
7265   {
7266     SPR_walk + 3,                       1,
7267     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
7268   },
7269   {
7270     SPR_push + 0,                       1,
7271     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
7272   },
7273   {
7274     SPR_push + 1,                       1,
7275     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
7276   },
7277   {
7278     SPR_push + 2,                       1,
7279     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
7280   },
7281   {
7282     SPR_push + 3,                       1,
7283     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
7284   },
7285   {
7286     SPR_spray + 0,                      1,
7287     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
7288   },
7289   {
7290     SPR_spray + 1,                      1,
7291     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7292   },
7293   {
7294     SPR_spray + 2,                      1,
7295     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
7296   },
7297   {
7298     SPR_spray + 3,                      1,
7299     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
7300   },
7301   {
7302     SPR_still,                          0,
7303     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
7304   },
7305   {
7306     SPR_still,                          1,
7307     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
7308   },
7309   {
7310     SPR_walk + 0,                       2,
7311     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
7312   },
7313   {
7314     SPR_walk + 1,                       2,
7315     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
7316   },
7317   {
7318     SPR_walk + 2,                       2,
7319     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
7320   },
7321   {
7322     SPR_walk + 3,                       2,
7323     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
7324   },
7325   {
7326     SPR_push + 0,                       2,
7327     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
7328   },
7329   {
7330     SPR_push + 1,                       2,
7331     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
7332   },
7333   {
7334     SPR_push + 2,                       2,
7335     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
7336   },
7337   {
7338     SPR_push + 3,                       2,
7339     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
7340   },
7341   {
7342     SPR_spray + 0,                      2,
7343     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
7344   },
7345   {
7346     SPR_spray + 1,                      2,
7347     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7348   },
7349   {
7350     SPR_spray + 2,                      2,
7351     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
7352   },
7353   {
7354     SPR_spray + 3,                      2,
7355     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
7356   },
7357   {
7358     SPR_walk + 0,                       3,
7359     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
7360   },
7361   {
7362     SPR_walk + 1,                       3,
7363     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
7364   },
7365   {
7366     SPR_walk + 2,                       3,
7367     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
7368   },
7369   {
7370     SPR_walk + 3,                       3,
7371     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
7372   },
7373   {
7374     SPR_push + 0,                       3,
7375     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
7376   },
7377   {
7378     SPR_push + 1,                       3,
7379     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
7380   },
7381   {
7382     SPR_push + 2,                       3,
7383     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
7384   },
7385   {
7386     SPR_push + 3,                       3,
7387     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
7388   },
7389   {
7390     SPR_spray + 0,                      3,
7391     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
7392   },
7393   {
7394     SPR_spray + 1,                      3,
7395     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7396   },
7397   {
7398     SPR_spray + 2,                      3,
7399     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
7400   },
7401   {
7402     SPR_spray + 3,                      3,
7403     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
7404   },
7405   {
7406     SPR_still,                          2,
7407     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
7408   },
7409   {
7410     SPR_still,                          3,
7411     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7412   },
7413
7414   {
7415     -1,                                 -1,
7416     -1,                                 -1, -1
7417   }
7418 };
7419
7420 int map_element_RND_to_EM(int element_rnd)
7421 {
7422   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7423   static boolean mapping_initialized = FALSE;
7424
7425   if (!mapping_initialized)
7426   {
7427     int i;
7428
7429     /* return "Xalpha_quest" for all undefined elements in mapping array */
7430     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7431       mapping_RND_to_EM[i] = Xalpha_quest;
7432
7433     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7434       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7435         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7436           em_object_mapping_list[i].element_em;
7437
7438     mapping_initialized = TRUE;
7439   }
7440
7441   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7442     return mapping_RND_to_EM[element_rnd];
7443
7444   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7445
7446   return EL_UNKNOWN;
7447 }
7448
7449 int map_element_EM_to_RND(int element_em)
7450 {
7451   static unsigned short mapping_EM_to_RND[TILE_MAX];
7452   static boolean mapping_initialized = FALSE;
7453
7454   if (!mapping_initialized)
7455   {
7456     int i;
7457
7458     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7459     for (i = 0; i < TILE_MAX; i++)
7460       mapping_EM_to_RND[i] = EL_UNKNOWN;
7461
7462     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7463       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7464         em_object_mapping_list[i].element_rnd;
7465
7466     mapping_initialized = TRUE;
7467   }
7468
7469   if (element_em >= 0 && element_em < TILE_MAX)
7470     return mapping_EM_to_RND[element_em];
7471
7472   Error(ERR_WARN, "invalid EM level element %d", element_em);
7473
7474   return EL_UNKNOWN;
7475 }
7476
7477 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7478 {
7479   struct LevelInfo_EM *level_em = level->native_em_level;
7480   struct LEVEL *lev = level_em->lev;
7481   int i, j;
7482
7483   for (i = 0; i < TILE_MAX; i++)
7484     lev->android_array[i] = Xblank;
7485
7486   for (i = 0; i < level->num_android_clone_elements; i++)
7487   {
7488     int element_rnd = level->android_clone_element[i];
7489     int element_em = map_element_RND_to_EM(element_rnd);
7490
7491     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7492       if (em_object_mapping_list[j].element_rnd == element_rnd)
7493         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7494   }
7495 }
7496
7497 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7498 {
7499   struct LevelInfo_EM *level_em = level->native_em_level;
7500   struct LEVEL *lev = level_em->lev;
7501   int i, j;
7502
7503   level->num_android_clone_elements = 0;
7504
7505   for (i = 0; i < TILE_MAX; i++)
7506   {
7507     int element_em = lev->android_array[i];
7508     int element_rnd;
7509     boolean element_found = FALSE;
7510
7511     if (element_em == Xblank)
7512       continue;
7513
7514     element_rnd = map_element_EM_to_RND(element_em);
7515
7516     for (j = 0; j < level->num_android_clone_elements; j++)
7517       if (level->android_clone_element[j] == element_rnd)
7518         element_found = TRUE;
7519
7520     if (!element_found)
7521     {
7522       level->android_clone_element[level->num_android_clone_elements++] =
7523         element_rnd;
7524
7525       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7526         break;
7527     }
7528   }
7529
7530   if (level->num_android_clone_elements == 0)
7531   {
7532     level->num_android_clone_elements = 1;
7533     level->android_clone_element[0] = EL_EMPTY;
7534   }
7535 }
7536
7537 int map_direction_RND_to_EM(int direction)
7538 {
7539   return (direction == MV_UP    ? 0 :
7540           direction == MV_RIGHT ? 1 :
7541           direction == MV_DOWN  ? 2 :
7542           direction == MV_LEFT  ? 3 :
7543           -1);
7544 }
7545
7546 int map_direction_EM_to_RND(int direction)
7547 {
7548   return (direction == 0 ? MV_UP    :
7549           direction == 1 ? MV_RIGHT :
7550           direction == 2 ? MV_DOWN  :
7551           direction == 3 ? MV_LEFT  :
7552           MV_NONE);
7553 }
7554
7555 int map_element_RND_to_SP(int element_rnd)
7556 {
7557   int element_sp = 0x20;        /* map unknown elements to yellow "hardware" */
7558
7559   if (element_rnd >= EL_SP_START &&
7560       element_rnd <= EL_SP_END)
7561     element_sp = element_rnd - EL_SP_START;
7562   else if (element_rnd == EL_EMPTY_SPACE)
7563     element_sp = 0x00;
7564   else if (element_rnd == EL_INVISIBLE_WALL)
7565     element_sp = 0x28;
7566
7567   return element_sp;
7568 }
7569
7570 int map_element_SP_to_RND(int element_sp)
7571 {
7572   int element_rnd = EL_UNKNOWN;
7573
7574   if (element_sp >= 0x00 &&
7575       element_sp <= 0x27)
7576     element_rnd = EL_SP_START + element_sp;
7577   else if (element_sp == 0x28)
7578     element_rnd = EL_INVISIBLE_WALL;
7579
7580   return element_rnd;
7581 }
7582
7583 int map_action_SP_to_RND(int action_sp)
7584 {
7585   switch (action_sp)
7586   {
7587     case actActive:             return ACTION_ACTIVE;
7588     case actImpact:             return ACTION_IMPACT;
7589     case actExploding:          return ACTION_EXPLODING;
7590     case actDigging:            return ACTION_DIGGING;
7591     case actSnapping:           return ACTION_SNAPPING;
7592     case actCollecting:         return ACTION_COLLECTING;
7593     case actPassing:            return ACTION_PASSING;
7594     case actPushing:            return ACTION_PUSHING;
7595     case actDropping:           return ACTION_DROPPING;
7596
7597     default:                    return ACTION_DEFAULT;
7598   }
7599 }
7600
7601 int map_element_RND_to_MM(int element_rnd)
7602 {
7603   return (element_rnd >= EL_MM_START_1 &&
7604           element_rnd <= EL_MM_END_1 ?
7605           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7606
7607           element_rnd >= EL_MM_START_2 &&
7608           element_rnd <= EL_MM_END_2 ?
7609           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7610
7611           element_rnd >= EL_CHAR_START &&
7612           element_rnd <= EL_CHAR_END ?
7613           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7614
7615           element_rnd >= EL_MM_RUNTIME_START &&
7616           element_rnd <= EL_MM_RUNTIME_END ?
7617           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7618
7619           element_rnd >= EL_MM_DUMMY_START &&
7620           element_rnd <= EL_MM_DUMMY_END ?
7621           EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7622
7623           EL_MM_EMPTY_NATIVE);
7624 }
7625
7626 int map_element_MM_to_RND(int element_mm)
7627 {
7628   return (element_mm == EL_MM_EMPTY_NATIVE ||
7629           element_mm == EL_DF_EMPTY_NATIVE ?
7630           EL_EMPTY :
7631
7632           element_mm >= EL_MM_START_1_NATIVE &&
7633           element_mm <= EL_MM_END_1_NATIVE ?
7634           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7635
7636           element_mm >= EL_MM_START_2_NATIVE &&
7637           element_mm <= EL_MM_END_2_NATIVE ?
7638           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7639
7640           element_mm >= EL_MM_CHAR_START_NATIVE &&
7641           element_mm <= EL_MM_CHAR_END_NATIVE ?
7642           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7643
7644           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7645           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7646           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7647
7648           element_mm >= EL_MM_DUMMY_START_NATIVE &&
7649           element_mm <= EL_MM_DUMMY_END_NATIVE ?
7650           EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7651
7652           EL_EMPTY);
7653 }
7654
7655 int map_action_MM_to_RND(int action_mm)
7656 {
7657   /* all MM actions are defined to exactly match their RND counterparts */
7658   return action_mm;
7659 }
7660
7661 int map_sound_MM_to_RND(int sound_mm)
7662 {
7663   switch (sound_mm)
7664   {
7665     case SND_MM_GAME_LEVELTIME_CHARGING:
7666       return SND_GAME_LEVELTIME_CHARGING;
7667
7668     case SND_MM_GAME_HEALTH_CHARGING:
7669       return SND_GAME_HEALTH_CHARGING;
7670
7671     default:
7672       return SND_UNDEFINED;
7673   }
7674 }
7675
7676 int map_mm_wall_element(int element)
7677 {
7678   return (element >= EL_MM_STEEL_WALL_START &&
7679           element <= EL_MM_STEEL_WALL_END ?
7680           EL_MM_STEEL_WALL :
7681
7682           element >= EL_MM_WOODEN_WALL_START &&
7683           element <= EL_MM_WOODEN_WALL_END ?
7684           EL_MM_WOODEN_WALL :
7685
7686           element >= EL_MM_ICE_WALL_START &&
7687           element <= EL_MM_ICE_WALL_END ?
7688           EL_MM_ICE_WALL :
7689
7690           element >= EL_MM_AMOEBA_WALL_START &&
7691           element <= EL_MM_AMOEBA_WALL_END ?
7692           EL_MM_AMOEBA_WALL :
7693
7694           element >= EL_DF_STEEL_WALL_START &&
7695           element <= EL_DF_STEEL_WALL_END ?
7696           EL_DF_STEEL_WALL :
7697
7698           element >= EL_DF_WOODEN_WALL_START &&
7699           element <= EL_DF_WOODEN_WALL_END ?
7700           EL_DF_WOODEN_WALL :
7701
7702           element);
7703 }
7704
7705 int map_mm_wall_element_editor(int element)
7706 {
7707   switch (element)
7708   {
7709     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
7710     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
7711     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
7712     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
7713     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
7714     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
7715
7716     default:                    return element;
7717   }
7718 }
7719
7720 int get_next_element(int element)
7721 {
7722   switch (element)
7723   {
7724     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
7725     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
7726     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
7727     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
7728     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
7729     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
7730     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
7731     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
7732     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
7733     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
7734     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
7735
7736     default:                            return element;
7737   }
7738 }
7739
7740 int el2img_mm(int element_mm)
7741 {
7742   return el2img(map_element_MM_to_RND(element_mm));
7743 }
7744
7745 int el_act_dir2img(int element, int action, int direction)
7746 {
7747   element = GFX_ELEMENT(element);
7748   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7749
7750   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7751   return element_info[element].direction_graphic[action][direction];
7752 }
7753
7754 static int el_act_dir2crm(int element, int action, int direction)
7755 {
7756   element = GFX_ELEMENT(element);
7757   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7758
7759   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7760   return element_info[element].direction_crumbled[action][direction];
7761 }
7762
7763 int el_act2img(int element, int action)
7764 {
7765   element = GFX_ELEMENT(element);
7766
7767   return element_info[element].graphic[action];
7768 }
7769
7770 int el_act2crm(int element, int action)
7771 {
7772   element = GFX_ELEMENT(element);
7773
7774   return element_info[element].crumbled[action];
7775 }
7776
7777 int el_dir2img(int element, int direction)
7778 {
7779   element = GFX_ELEMENT(element);
7780
7781   return el_act_dir2img(element, ACTION_DEFAULT, direction);
7782 }
7783
7784 int el2baseimg(int element)
7785 {
7786   return element_info[element].graphic[ACTION_DEFAULT];
7787 }
7788
7789 int el2img(int element)
7790 {
7791   element = GFX_ELEMENT(element);
7792
7793   return element_info[element].graphic[ACTION_DEFAULT];
7794 }
7795
7796 int el2edimg(int element)
7797 {
7798   element = GFX_ELEMENT(element);
7799
7800   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7801 }
7802
7803 int el2preimg(int element)
7804 {
7805   element = GFX_ELEMENT(element);
7806
7807   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7808 }
7809
7810 int el2panelimg(int element)
7811 {
7812   element = GFX_ELEMENT(element);
7813
7814   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7815 }
7816
7817 int font2baseimg(int font_nr)
7818 {
7819   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7820 }
7821
7822 int getBeltNrFromBeltElement(int element)
7823 {
7824   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7825           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7826           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7827 }
7828
7829 int getBeltNrFromBeltActiveElement(int element)
7830 {
7831   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7832           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7833           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7834 }
7835
7836 int getBeltNrFromBeltSwitchElement(int element)
7837 {
7838   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7839           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7840           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7841 }
7842
7843 int getBeltDirNrFromBeltElement(int element)
7844 {
7845   static int belt_base_element[4] =
7846   {
7847     EL_CONVEYOR_BELT_1_LEFT,
7848     EL_CONVEYOR_BELT_2_LEFT,
7849     EL_CONVEYOR_BELT_3_LEFT,
7850     EL_CONVEYOR_BELT_4_LEFT
7851   };
7852
7853   int belt_nr = getBeltNrFromBeltElement(element);
7854   int belt_dir_nr = element - belt_base_element[belt_nr];
7855
7856   return (belt_dir_nr % 3);
7857 }
7858
7859 int getBeltDirNrFromBeltSwitchElement(int element)
7860 {
7861   static int belt_base_element[4] =
7862   {
7863     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7864     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7865     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7866     EL_CONVEYOR_BELT_4_SWITCH_LEFT
7867   };
7868
7869   int belt_nr = getBeltNrFromBeltSwitchElement(element);
7870   int belt_dir_nr = element - belt_base_element[belt_nr];
7871
7872   return (belt_dir_nr % 3);
7873 }
7874
7875 int getBeltDirFromBeltElement(int element)
7876 {
7877   static int belt_move_dir[3] =
7878   {
7879     MV_LEFT,
7880     MV_NONE,
7881     MV_RIGHT
7882   };
7883
7884   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7885
7886   return belt_move_dir[belt_dir_nr];
7887 }
7888
7889 int getBeltDirFromBeltSwitchElement(int element)
7890 {
7891   static int belt_move_dir[3] =
7892   {
7893     MV_LEFT,
7894     MV_NONE,
7895     MV_RIGHT
7896   };
7897
7898   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7899
7900   return belt_move_dir[belt_dir_nr];
7901 }
7902
7903 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7904 {
7905   static int belt_base_element[4] =
7906   {
7907     EL_CONVEYOR_BELT_1_LEFT,
7908     EL_CONVEYOR_BELT_2_LEFT,
7909     EL_CONVEYOR_BELT_3_LEFT,
7910     EL_CONVEYOR_BELT_4_LEFT
7911   };
7912
7913   return belt_base_element[belt_nr] + belt_dir_nr;
7914 }
7915
7916 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7917 {
7918   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7919
7920   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7921 }
7922
7923 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7924 {
7925   static int belt_base_element[4] =
7926   {
7927     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7928     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7929     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7930     EL_CONVEYOR_BELT_4_SWITCH_LEFT
7931   };
7932
7933   return belt_base_element[belt_nr] + belt_dir_nr;
7934 }
7935
7936 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7937 {
7938   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7939
7940   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7941 }
7942
7943 boolean getTeamMode_EM()
7944 {
7945   return game.team_mode;
7946 }
7947
7948 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7949 {
7950   int game_frame_delay_value;
7951
7952   game_frame_delay_value =
7953     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7954      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7955      GameFrameDelay);
7956
7957   if (tape.playing && tape.warp_forward && !tape.pausing)
7958     game_frame_delay_value = 0;
7959
7960   return game_frame_delay_value;
7961 }
7962
7963 unsigned int InitRND(int seed)
7964 {
7965   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7966     return InitEngineRandom_EM(seed);
7967   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7968     return InitEngineRandom_SP(seed);
7969   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7970     return InitEngineRandom_MM(seed);
7971   else
7972     return InitEngineRandom_RND(seed);
7973 }
7974
7975 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7976 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7977
7978 inline static int get_effective_element_EM(int tile, int frame_em)
7979 {
7980   int element             = object_mapping[tile].element_rnd;
7981   int action              = object_mapping[tile].action;
7982   boolean is_backside     = object_mapping[tile].is_backside;
7983   boolean action_removing = (action == ACTION_DIGGING ||
7984                              action == ACTION_SNAPPING ||
7985                              action == ACTION_COLLECTING);
7986
7987   if (frame_em < 7)
7988   {
7989     switch (tile)
7990     {
7991       case Yacid_splash_eB:
7992       case Yacid_splash_wB:
7993         return (frame_em > 5 ? EL_EMPTY : element);
7994
7995       default:
7996         return element;
7997     }
7998   }
7999   else  /* frame_em == 7 */
8000   {
8001     switch (tile)
8002     {
8003       case Yacid_splash_eB:
8004       case Yacid_splash_wB:
8005         return EL_EMPTY;
8006
8007       case Yemerald_stone:
8008         return EL_EMERALD;
8009
8010       case Ydiamond_stone:
8011         return EL_ROCK;
8012
8013       case Xdrip_stretch:
8014       case Xdrip_stretchB:
8015       case Ydrip_s1:
8016       case Ydrip_s1B:
8017       case Xball_1B:
8018       case Xball_2:
8019       case Xball_2B:
8020       case Yball_eat:
8021       case Ykey_1_eat:
8022       case Ykey_2_eat:
8023       case Ykey_3_eat:
8024       case Ykey_4_eat:
8025       case Ykey_5_eat:
8026       case Ykey_6_eat:
8027       case Ykey_7_eat:
8028       case Ykey_8_eat:
8029       case Ylenses_eat:
8030       case Ymagnify_eat:
8031       case Ygrass_eat:
8032       case Ydirt_eat:
8033       case Xsand_stonein_1:
8034       case Xsand_stonein_2:
8035       case Xsand_stonein_3:
8036       case Xsand_stonein_4:
8037         return element;
8038
8039       default:
8040         return (is_backside || action_removing ? EL_EMPTY : element);
8041     }
8042   }
8043 }
8044
8045 inline static boolean check_linear_animation_EM(int tile)
8046 {
8047   switch (tile)
8048   {
8049     case Xsand_stonesand_1:
8050     case Xsand_stonesand_quickout_1:
8051     case Xsand_sandstone_1:
8052     case Xsand_stonein_1:
8053     case Xsand_stoneout_1:
8054     case Xboom_1:
8055     case Xdynamite_1:
8056     case Ybug_w_n:
8057     case Ybug_n_e:
8058     case Ybug_e_s:
8059     case Ybug_s_w:
8060     case Ybug_e_n:
8061     case Ybug_s_e:
8062     case Ybug_w_s:
8063     case Ybug_n_w:
8064     case Ytank_w_n:
8065     case Ytank_n_e:
8066     case Ytank_e_s:
8067     case Ytank_s_w:
8068     case Ytank_e_n:
8069     case Ytank_s_e:
8070     case Ytank_w_s:
8071     case Ytank_n_w:
8072     case Yacid_splash_eB:
8073     case Yacid_splash_wB:
8074     case Yemerald_stone:
8075       return TRUE;
8076   }
8077
8078   return FALSE;
8079 }
8080
8081 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8082                                             boolean has_crumbled_graphics,
8083                                             int crumbled, int sync_frame)
8084 {
8085   /* if element can be crumbled, but certain action graphics are just empty
8086      space (like instantly snapping sand to empty space in 1 frame), do not
8087      treat these empty space graphics as crumbled graphics in EMC engine */
8088   if (crumbled == IMG_EMPTY_SPACE)
8089     has_crumbled_graphics = FALSE;
8090
8091   if (has_crumbled_graphics)
8092   {
8093     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8094     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8095                                            g_crumbled->anim_delay,
8096                                            g_crumbled->anim_mode,
8097                                            g_crumbled->anim_start_frame,
8098                                            sync_frame);
8099
8100     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8101                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8102
8103     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8104     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8105
8106     g_em->has_crumbled_graphics = TRUE;
8107   }
8108   else
8109   {
8110     g_em->crumbled_bitmap = NULL;
8111     g_em->crumbled_src_x = 0;
8112     g_em->crumbled_src_y = 0;
8113     g_em->crumbled_border_size = 0;
8114     g_em->crumbled_tile_size = 0;
8115
8116     g_em->has_crumbled_graphics = FALSE;
8117   }
8118 }
8119
8120 void ResetGfxAnimation_EM(int x, int y, int tile)
8121 {
8122   GfxFrame[x][y] = 0;
8123 }
8124
8125 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8126                         int tile, int frame_em, int x, int y)
8127 {
8128   int action = object_mapping[tile].action;
8129   int direction = object_mapping[tile].direction;
8130   int effective_element = get_effective_element_EM(tile, frame_em);
8131   int graphic = (direction == MV_NONE ?
8132                  el_act2img(effective_element, action) :
8133                  el_act_dir2img(effective_element, action, direction));
8134   struct GraphicInfo *g = &graphic_info[graphic];
8135   int sync_frame;
8136   boolean action_removing = (action == ACTION_DIGGING ||
8137                              action == ACTION_SNAPPING ||
8138                              action == ACTION_COLLECTING);
8139   boolean action_moving   = (action == ACTION_FALLING ||
8140                              action == ACTION_MOVING ||
8141                              action == ACTION_PUSHING ||
8142                              action == ACTION_EATING ||
8143                              action == ACTION_FILLING ||
8144                              action == ACTION_EMPTYING);
8145   boolean action_falling  = (action == ACTION_FALLING ||
8146                              action == ACTION_FILLING ||
8147                              action == ACTION_EMPTYING);
8148
8149   /* special case: graphic uses "2nd movement tile" and has defined
8150      7 frames for movement animation (or less) => use default graphic
8151      for last (8th) frame which ends the movement animation */
8152   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8153   {
8154     action = ACTION_DEFAULT;    /* (keep action_* unchanged for now) */
8155     graphic = (direction == MV_NONE ?
8156                el_act2img(effective_element, action) :
8157                el_act_dir2img(effective_element, action, direction));
8158
8159     g = &graphic_info[graphic];
8160   }
8161
8162   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8163   {
8164     GfxFrame[x][y] = 0;
8165   }
8166   else if (action_moving)
8167   {
8168     boolean is_backside = object_mapping[tile].is_backside;
8169
8170     if (is_backside)
8171     {
8172       int direction = object_mapping[tile].direction;
8173       int move_dir = (action_falling ? MV_DOWN : direction);
8174
8175       GfxFrame[x][y]++;
8176
8177 #if 1
8178       /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8179       if (g->double_movement && frame_em == 0)
8180         GfxFrame[x][y] = 0;
8181 #endif
8182
8183       if (move_dir == MV_LEFT)
8184         GfxFrame[x - 1][y] = GfxFrame[x][y];
8185       else if (move_dir == MV_RIGHT)
8186         GfxFrame[x + 1][y] = GfxFrame[x][y];
8187       else if (move_dir == MV_UP)
8188         GfxFrame[x][y - 1] = GfxFrame[x][y];
8189       else if (move_dir == MV_DOWN)
8190         GfxFrame[x][y + 1] = GfxFrame[x][y];
8191     }
8192   }
8193   else
8194   {
8195     GfxFrame[x][y]++;
8196
8197     /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8198     if (tile == Xsand_stonesand_quickout_1 ||
8199         tile == Xsand_stonesand_quickout_2)
8200       GfxFrame[x][y]++;
8201   }
8202
8203   if (graphic_info[graphic].anim_global_sync)
8204     sync_frame = FrameCounter;
8205   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8206     sync_frame = GfxFrame[x][y];
8207   else
8208     sync_frame = 0;     /* playfield border (pseudo steel) */
8209
8210   SetRandomAnimationValue(x, y);
8211
8212   int frame = getAnimationFrame(g->anim_frames,
8213                                 g->anim_delay,
8214                                 g->anim_mode,
8215                                 g->anim_start_frame,
8216                                 sync_frame);
8217
8218   g_em->unique_identifier =
8219     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8220 }
8221
8222 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8223                                   int tile, int frame_em, int x, int y)
8224 {
8225   int action = object_mapping[tile].action;
8226   int direction = object_mapping[tile].direction;
8227   boolean is_backside = object_mapping[tile].is_backside;
8228   int effective_element = get_effective_element_EM(tile, frame_em);
8229   int effective_action = action;
8230   int graphic = (direction == MV_NONE ?
8231                  el_act2img(effective_element, effective_action) :
8232                  el_act_dir2img(effective_element, effective_action,
8233                                 direction));
8234   int crumbled = (direction == MV_NONE ?
8235                   el_act2crm(effective_element, effective_action) :
8236                   el_act_dir2crm(effective_element, effective_action,
8237                                  direction));
8238   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8239   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8240   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8241   struct GraphicInfo *g = &graphic_info[graphic];
8242   int sync_frame;
8243
8244   /* special case: graphic uses "2nd movement tile" and has defined
8245      7 frames for movement animation (or less) => use default graphic
8246      for last (8th) frame which ends the movement animation */
8247   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8248   {
8249     effective_action = ACTION_DEFAULT;
8250     graphic = (direction == MV_NONE ?
8251                el_act2img(effective_element, effective_action) :
8252                el_act_dir2img(effective_element, effective_action,
8253                               direction));
8254     crumbled = (direction == MV_NONE ?
8255                 el_act2crm(effective_element, effective_action) :
8256                 el_act_dir2crm(effective_element, effective_action,
8257                                direction));
8258
8259     g = &graphic_info[graphic];
8260   }
8261
8262   if (graphic_info[graphic].anim_global_sync)
8263     sync_frame = FrameCounter;
8264   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8265     sync_frame = GfxFrame[x][y];
8266   else
8267     sync_frame = 0;     /* playfield border (pseudo steel) */
8268
8269   SetRandomAnimationValue(x, y);
8270
8271   int frame = getAnimationFrame(g->anim_frames,
8272                                 g->anim_delay,
8273                                 g->anim_mode,
8274                                 g->anim_start_frame,
8275                                 sync_frame);
8276
8277   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8278                       g->double_movement && is_backside);
8279
8280   /* (updating the "crumbled" graphic definitions is probably not really needed,
8281      as animations for crumbled graphics can't be longer than one EMC cycle) */
8282   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8283                            sync_frame);
8284 }
8285
8286 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8287                                   int player_nr, int anim, int frame_em)
8288 {
8289   int element   = player_mapping[player_nr][anim].element_rnd;
8290   int action    = player_mapping[player_nr][anim].action;
8291   int direction = player_mapping[player_nr][anim].direction;
8292   int graphic = (direction == MV_NONE ?
8293                  el_act2img(element, action) :
8294                  el_act_dir2img(element, action, direction));
8295   struct GraphicInfo *g = &graphic_info[graphic];
8296   int sync_frame;
8297
8298   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8299
8300   stored_player[player_nr].StepFrame = frame_em;
8301
8302   sync_frame = stored_player[player_nr].Frame;
8303
8304   int frame = getAnimationFrame(g->anim_frames,
8305                                 g->anim_delay,
8306                                 g->anim_mode,
8307                                 g->anim_start_frame,
8308                                 sync_frame);
8309
8310   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8311                       &g_em->src_x, &g_em->src_y, FALSE);
8312 }
8313
8314 void InitGraphicInfo_EM(void)
8315 {
8316   int i, j, p;
8317
8318 #if DEBUG_EM_GFX
8319   int num_em_gfx_errors = 0;
8320
8321   if (graphic_info_em_object[0][0].bitmap == NULL)
8322   {
8323     /* EM graphics not yet initialized in em_open_all() */
8324
8325     return;
8326   }
8327
8328   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8329 #endif
8330
8331   /* always start with reliable default values */
8332   for (i = 0; i < TILE_MAX; i++)
8333   {
8334     object_mapping[i].element_rnd = EL_UNKNOWN;
8335     object_mapping[i].is_backside = FALSE;
8336     object_mapping[i].action = ACTION_DEFAULT;
8337     object_mapping[i].direction = MV_NONE;
8338   }
8339
8340   /* always start with reliable default values */
8341   for (p = 0; p < MAX_PLAYERS; p++)
8342   {
8343     for (i = 0; i < SPR_MAX; i++)
8344     {
8345       player_mapping[p][i].element_rnd = EL_UNKNOWN;
8346       player_mapping[p][i].action = ACTION_DEFAULT;
8347       player_mapping[p][i].direction = MV_NONE;
8348     }
8349   }
8350
8351   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8352   {
8353     int e = em_object_mapping_list[i].element_em;
8354
8355     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8356     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8357
8358     if (em_object_mapping_list[i].action != -1)
8359       object_mapping[e].action = em_object_mapping_list[i].action;
8360
8361     if (em_object_mapping_list[i].direction != -1)
8362       object_mapping[e].direction =
8363         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8364   }
8365
8366   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8367   {
8368     int a = em_player_mapping_list[i].action_em;
8369     int p = em_player_mapping_list[i].player_nr;
8370
8371     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8372
8373     if (em_player_mapping_list[i].action != -1)
8374       player_mapping[p][a].action = em_player_mapping_list[i].action;
8375
8376     if (em_player_mapping_list[i].direction != -1)
8377       player_mapping[p][a].direction =
8378         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8379   }
8380
8381   for (i = 0; i < TILE_MAX; i++)
8382   {
8383     int element = object_mapping[i].element_rnd;
8384     int action = object_mapping[i].action;
8385     int direction = object_mapping[i].direction;
8386     boolean is_backside = object_mapping[i].is_backside;
8387     boolean action_exploding = ((action == ACTION_EXPLODING ||
8388                                  action == ACTION_SMASHED_BY_ROCK ||
8389                                  action == ACTION_SMASHED_BY_SPRING) &&
8390                                 element != EL_DIAMOND);
8391     boolean action_active = (action == ACTION_ACTIVE);
8392     boolean action_other = (action == ACTION_OTHER);
8393
8394     for (j = 0; j < 8; j++)
8395     {
8396       int effective_element = get_effective_element_EM(i, j);
8397       int effective_action = (j < 7 ? action :
8398                               i == Xdrip_stretch ? action :
8399                               i == Xdrip_stretchB ? action :
8400                               i == Ydrip_s1 ? action :
8401                               i == Ydrip_s1B ? action :
8402                               i == Xball_1B ? action :
8403                               i == Xball_2 ? action :
8404                               i == Xball_2B ? action :
8405                               i == Yball_eat ? action :
8406                               i == Ykey_1_eat ? action :
8407                               i == Ykey_2_eat ? action :
8408                               i == Ykey_3_eat ? action :
8409                               i == Ykey_4_eat ? action :
8410                               i == Ykey_5_eat ? action :
8411                               i == Ykey_6_eat ? action :
8412                               i == Ykey_7_eat ? action :
8413                               i == Ykey_8_eat ? action :
8414                               i == Ylenses_eat ? action :
8415                               i == Ymagnify_eat ? action :
8416                               i == Ygrass_eat ? action :
8417                               i == Ydirt_eat ? action :
8418                               i == Xsand_stonein_1 ? action :
8419                               i == Xsand_stonein_2 ? action :
8420                               i == Xsand_stonein_3 ? action :
8421                               i == Xsand_stonein_4 ? action :
8422                               i == Xsand_stoneout_1 ? action :
8423                               i == Xsand_stoneout_2 ? action :
8424                               i == Xboom_android ? ACTION_EXPLODING :
8425                               action_exploding ? ACTION_EXPLODING :
8426                               action_active ? action :
8427                               action_other ? action :
8428                               ACTION_DEFAULT);
8429       int graphic = (el_act_dir2img(effective_element, effective_action,
8430                                     direction));
8431       int crumbled = (el_act_dir2crm(effective_element, effective_action,
8432                                      direction));
8433       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8434       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8435       boolean has_action_graphics = (graphic != base_graphic);
8436       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8437       struct GraphicInfo *g = &graphic_info[graphic];
8438       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8439       Bitmap *src_bitmap;
8440       int src_x, src_y;
8441       /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8442       boolean special_animation = (action != ACTION_DEFAULT &&
8443                                    g->anim_frames == 3 &&
8444                                    g->anim_delay == 2 &&
8445                                    g->anim_mode & ANIM_LINEAR);
8446       int sync_frame = (i == Xdrip_stretch ? 7 :
8447                         i == Xdrip_stretchB ? 7 :
8448                         i == Ydrip_s2 ? j + 8 :
8449                         i == Ydrip_s2B ? j + 8 :
8450                         i == Xacid_1 ? 0 :
8451                         i == Xacid_2 ? 10 :
8452                         i == Xacid_3 ? 20 :
8453                         i == Xacid_4 ? 30 :
8454                         i == Xacid_5 ? 40 :
8455                         i == Xacid_6 ? 50 :
8456                         i == Xacid_7 ? 60 :
8457                         i == Xacid_8 ? 70 :
8458                         i == Xfake_acid_1 ? 0 :
8459                         i == Xfake_acid_2 ? 10 :
8460                         i == Xfake_acid_3 ? 20 :
8461                         i == Xfake_acid_4 ? 30 :
8462                         i == Xfake_acid_5 ? 40 :
8463                         i == Xfake_acid_6 ? 50 :
8464                         i == Xfake_acid_7 ? 60 :
8465                         i == Xfake_acid_8 ? 70 :
8466                         i == Xball_2 ? 7 :
8467                         i == Xball_2B ? j + 8 :
8468                         i == Yball_eat ? j + 1 :
8469                         i == Ykey_1_eat ? j + 1 :
8470                         i == Ykey_2_eat ? j + 1 :
8471                         i == Ykey_3_eat ? j + 1 :
8472                         i == Ykey_4_eat ? j + 1 :
8473                         i == Ykey_5_eat ? j + 1 :
8474                         i == Ykey_6_eat ? j + 1 :
8475                         i == Ykey_7_eat ? j + 1 :
8476                         i == Ykey_8_eat ? j + 1 :
8477                         i == Ylenses_eat ? j + 1 :
8478                         i == Ymagnify_eat ? j + 1 :
8479                         i == Ygrass_eat ? j + 1 :
8480                         i == Ydirt_eat ? j + 1 :
8481                         i == Xamoeba_1 ? 0 :
8482                         i == Xamoeba_2 ? 1 :
8483                         i == Xamoeba_3 ? 2 :
8484                         i == Xamoeba_4 ? 3 :
8485                         i == Xamoeba_5 ? 0 :
8486                         i == Xamoeba_6 ? 1 :
8487                         i == Xamoeba_7 ? 2 :
8488                         i == Xamoeba_8 ? 3 :
8489                         i == Xexit_2 ? j + 8 :
8490                         i == Xexit_3 ? j + 16 :
8491                         i == Xdynamite_1 ? 0 :
8492                         i == Xdynamite_2 ? 8 :
8493                         i == Xdynamite_3 ? 16 :
8494                         i == Xdynamite_4 ? 24 :
8495                         i == Xsand_stonein_1 ? j + 1 :
8496                         i == Xsand_stonein_2 ? j + 9 :
8497                         i == Xsand_stonein_3 ? j + 17 :
8498                         i == Xsand_stonein_4 ? j + 25 :
8499                         i == Xsand_stoneout_1 && j == 0 ? 0 :
8500                         i == Xsand_stoneout_1 && j == 1 ? 0 :
8501                         i == Xsand_stoneout_1 && j == 2 ? 1 :
8502                         i == Xsand_stoneout_1 && j == 3 ? 2 :
8503                         i == Xsand_stoneout_1 && j == 4 ? 2 :
8504                         i == Xsand_stoneout_1 && j == 5 ? 3 :
8505                         i == Xsand_stoneout_1 && j == 6 ? 4 :
8506                         i == Xsand_stoneout_1 && j == 7 ? 4 :
8507                         i == Xsand_stoneout_2 && j == 0 ? 5 :
8508                         i == Xsand_stoneout_2 && j == 1 ? 6 :
8509                         i == Xsand_stoneout_2 && j == 2 ? 7 :
8510                         i == Xsand_stoneout_2 && j == 3 ? 8 :
8511                         i == Xsand_stoneout_2 && j == 4 ? 9 :
8512                         i == Xsand_stoneout_2 && j == 5 ? 11 :
8513                         i == Xsand_stoneout_2 && j == 6 ? 13 :
8514                         i == Xsand_stoneout_2 && j == 7 ? 15 :
8515                         i == Xboom_bug && j == 1 ? 2 :
8516                         i == Xboom_bug && j == 2 ? 2 :
8517                         i == Xboom_bug && j == 3 ? 4 :
8518                         i == Xboom_bug && j == 4 ? 4 :
8519                         i == Xboom_bug && j == 5 ? 2 :
8520                         i == Xboom_bug && j == 6 ? 2 :
8521                         i == Xboom_bug && j == 7 ? 0 :
8522                         i == Xboom_bomb && j == 1 ? 2 :
8523                         i == Xboom_bomb && j == 2 ? 2 :
8524                         i == Xboom_bomb && j == 3 ? 4 :
8525                         i == Xboom_bomb && j == 4 ? 4 :
8526                         i == Xboom_bomb && j == 5 ? 2 :
8527                         i == Xboom_bomb && j == 6 ? 2 :
8528                         i == Xboom_bomb && j == 7 ? 0 :
8529                         i == Xboom_android && j == 7 ? 6 :
8530                         i == Xboom_1 && j == 1 ? 2 :
8531                         i == Xboom_1 && j == 2 ? 2 :
8532                         i == Xboom_1 && j == 3 ? 4 :
8533                         i == Xboom_1 && j == 4 ? 4 :
8534                         i == Xboom_1 && j == 5 ? 6 :
8535                         i == Xboom_1 && j == 6 ? 6 :
8536                         i == Xboom_1 && j == 7 ? 8 :
8537                         i == Xboom_2 && j == 0 ? 8 :
8538                         i == Xboom_2 && j == 1 ? 8 :
8539                         i == Xboom_2 && j == 2 ? 10 :
8540                         i == Xboom_2 && j == 3 ? 10 :
8541                         i == Xboom_2 && j == 4 ? 10 :
8542                         i == Xboom_2 && j == 5 ? 12 :
8543                         i == Xboom_2 && j == 6 ? 12 :
8544                         i == Xboom_2 && j == 7 ? 12 :
8545                         special_animation && j == 4 ? 3 :
8546                         effective_action != action ? 0 :
8547                         j);
8548
8549 #if DEBUG_EM_GFX
8550       Bitmap *debug_bitmap = g_em->bitmap;
8551       int debug_src_x = g_em->src_x;
8552       int debug_src_y = g_em->src_y;
8553 #endif
8554
8555       int frame = getAnimationFrame(g->anim_frames,
8556                                     g->anim_delay,
8557                                     g->anim_mode,
8558                                     g->anim_start_frame,
8559                                     sync_frame);
8560
8561       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8562                           g->double_movement && is_backside);
8563
8564       g_em->bitmap = src_bitmap;
8565       g_em->src_x = src_x;
8566       g_em->src_y = src_y;
8567       g_em->src_offset_x = 0;
8568       g_em->src_offset_y = 0;
8569       g_em->dst_offset_x = 0;
8570       g_em->dst_offset_y = 0;
8571       g_em->width  = TILEX;
8572       g_em->height = TILEY;
8573
8574       g_em->preserve_background = FALSE;
8575
8576       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8577                                sync_frame);
8578
8579       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8580                                    effective_action == ACTION_MOVING  ||
8581                                    effective_action == ACTION_PUSHING ||
8582                                    effective_action == ACTION_EATING)) ||
8583           (!has_action_graphics && (effective_action == ACTION_FILLING ||
8584                                     effective_action == ACTION_EMPTYING)))
8585       {
8586         int move_dir =
8587           (effective_action == ACTION_FALLING ||
8588            effective_action == ACTION_FILLING ||
8589            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8590         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8591         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
8592         int num_steps = (i == Ydrip_s1  ? 16 :
8593                          i == Ydrip_s1B ? 16 :
8594                          i == Ydrip_s2  ? 16 :
8595                          i == Ydrip_s2B ? 16 :
8596                          i == Xsand_stonein_1 ? 32 :
8597                          i == Xsand_stonein_2 ? 32 :
8598                          i == Xsand_stonein_3 ? 32 :
8599                          i == Xsand_stonein_4 ? 32 :
8600                          i == Xsand_stoneout_1 ? 16 :
8601                          i == Xsand_stoneout_2 ? 16 : 8);
8602         int cx = ABS(dx) * (TILEX / num_steps);
8603         int cy = ABS(dy) * (TILEY / num_steps);
8604         int step_frame = (i == Ydrip_s2         ? j + 8 :
8605                           i == Ydrip_s2B        ? j + 8 :
8606                           i == Xsand_stonein_2  ? j + 8 :
8607                           i == Xsand_stonein_3  ? j + 16 :
8608                           i == Xsand_stonein_4  ? j + 24 :
8609                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8610         int step = (is_backside ? step_frame : num_steps - step_frame);
8611
8612         if (is_backside)        /* tile where movement starts */
8613         {
8614           if (dx < 0 || dy < 0)
8615           {
8616             g_em->src_offset_x = cx * step;
8617             g_em->src_offset_y = cy * step;
8618           }
8619           else
8620           {
8621             g_em->dst_offset_x = cx * step;
8622             g_em->dst_offset_y = cy * step;
8623           }
8624         }
8625         else                    /* tile where movement ends */
8626         {
8627           if (dx < 0 || dy < 0)
8628           {
8629             g_em->dst_offset_x = cx * step;
8630             g_em->dst_offset_y = cy * step;
8631           }
8632           else
8633           {
8634             g_em->src_offset_x = cx * step;
8635             g_em->src_offset_y = cy * step;
8636           }
8637         }
8638
8639         g_em->width  = TILEX - cx * step;
8640         g_em->height = TILEY - cy * step;
8641       }
8642
8643       /* create unique graphic identifier to decide if tile must be redrawn */
8644       /* bit 31 - 16 (16 bit): EM style graphic
8645          bit 15 - 12 ( 4 bit): EM style frame
8646          bit 11 -  6 ( 6 bit): graphic width
8647          bit  5 -  0 ( 6 bit): graphic height */
8648       g_em->unique_identifier =
8649         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8650
8651 #if DEBUG_EM_GFX
8652
8653       /* skip check for EMC elements not contained in original EMC artwork */
8654       if (element == EL_EMC_FAKE_ACID)
8655         continue;
8656
8657       if (g_em->bitmap != debug_bitmap ||
8658           g_em->src_x != debug_src_x ||
8659           g_em->src_y != debug_src_y ||
8660           g_em->src_offset_x != 0 ||
8661           g_em->src_offset_y != 0 ||
8662           g_em->dst_offset_x != 0 ||
8663           g_em->dst_offset_y != 0 ||
8664           g_em->width != TILEX ||
8665           g_em->height != TILEY)
8666       {
8667         static int last_i = -1;
8668
8669         if (i != last_i)
8670         {
8671           printf("\n");
8672           last_i = i;
8673         }
8674
8675         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8676                i, element, element_info[element].token_name,
8677                element_action_info[effective_action].suffix, direction);
8678
8679         if (element != effective_element)
8680           printf(" [%d ('%s')]",
8681                  effective_element,
8682                  element_info[effective_element].token_name);
8683
8684         printf("\n");
8685
8686         if (g_em->bitmap != debug_bitmap)
8687           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8688                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8689
8690         if (g_em->src_x != debug_src_x ||
8691             g_em->src_y != debug_src_y)
8692           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8693                  j, (is_backside ? 'B' : 'F'),
8694                  g_em->src_x, g_em->src_y,
8695                  g_em->src_x / 32, g_em->src_y / 32,
8696                  debug_src_x, debug_src_y,
8697                  debug_src_x / 32, debug_src_y / 32);
8698
8699         if (g_em->src_offset_x != 0 ||
8700             g_em->src_offset_y != 0 ||
8701             g_em->dst_offset_x != 0 ||
8702             g_em->dst_offset_y != 0)
8703           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8704                  j, is_backside,
8705                  g_em->src_offset_x, g_em->src_offset_y,
8706                  g_em->dst_offset_x, g_em->dst_offset_y);
8707
8708         if (g_em->width != TILEX ||
8709             g_em->height != TILEY)
8710           printf("    %d (%d): size %d,%d should be %d,%d\n",
8711                  j, is_backside,
8712                  g_em->width, g_em->height, TILEX, TILEY);
8713
8714         num_em_gfx_errors++;
8715       }
8716 #endif
8717
8718     }
8719   }
8720
8721   for (i = 0; i < TILE_MAX; i++)
8722   {
8723     for (j = 0; j < 8; j++)
8724     {
8725       int element = object_mapping[i].element_rnd;
8726       int action = object_mapping[i].action;
8727       int direction = object_mapping[i].direction;
8728       boolean is_backside = object_mapping[i].is_backside;
8729       int graphic_action  = el_act_dir2img(element, action, direction);
8730       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8731
8732       if ((action == ACTION_SMASHED_BY_ROCK ||
8733            action == ACTION_SMASHED_BY_SPRING ||
8734            action == ACTION_EATING) &&
8735           graphic_action == graphic_default)
8736       {
8737         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
8738                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8739                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
8740                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8741                  Xspring);
8742
8743         /* no separate animation for "smashed by rock" -- use rock instead */
8744         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8745         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8746
8747         g_em->bitmap            = g_xx->bitmap;
8748         g_em->src_x             = g_xx->src_x;
8749         g_em->src_y             = g_xx->src_y;
8750         g_em->src_offset_x      = g_xx->src_offset_x;
8751         g_em->src_offset_y      = g_xx->src_offset_y;
8752         g_em->dst_offset_x      = g_xx->dst_offset_x;
8753         g_em->dst_offset_y      = g_xx->dst_offset_y;
8754         g_em->width             = g_xx->width;
8755         g_em->height            = g_xx->height;
8756         g_em->unique_identifier = g_xx->unique_identifier;
8757
8758         if (!is_backside)
8759           g_em->preserve_background = TRUE;
8760       }
8761     }
8762   }
8763
8764   for (p = 0; p < MAX_PLAYERS; p++)
8765   {
8766     for (i = 0; i < SPR_MAX; i++)
8767     {
8768       int element = player_mapping[p][i].element_rnd;
8769       int action = player_mapping[p][i].action;
8770       int direction = player_mapping[p][i].direction;
8771
8772       for (j = 0; j < 8; j++)
8773       {
8774         int effective_element = element;
8775         int effective_action = action;
8776         int graphic = (direction == MV_NONE ?
8777                        el_act2img(effective_element, effective_action) :
8778                        el_act_dir2img(effective_element, effective_action,
8779                                       direction));
8780         struct GraphicInfo *g = &graphic_info[graphic];
8781         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8782         Bitmap *src_bitmap;
8783         int src_x, src_y;
8784         int sync_frame = j;
8785
8786 #if DEBUG_EM_GFX
8787         Bitmap *debug_bitmap = g_em->bitmap;
8788         int debug_src_x = g_em->src_x;
8789         int debug_src_y = g_em->src_y;
8790 #endif
8791
8792         int frame = getAnimationFrame(g->anim_frames,
8793                                       g->anim_delay,
8794                                       g->anim_mode,
8795                                       g->anim_start_frame,
8796                                       sync_frame);
8797
8798         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8799
8800         g_em->bitmap = src_bitmap;
8801         g_em->src_x = src_x;
8802         g_em->src_y = src_y;
8803         g_em->src_offset_x = 0;
8804         g_em->src_offset_y = 0;
8805         g_em->dst_offset_x = 0;
8806         g_em->dst_offset_y = 0;
8807         g_em->width  = TILEX;
8808         g_em->height = TILEY;
8809
8810 #if DEBUG_EM_GFX
8811
8812         /* skip check for EMC elements not contained in original EMC artwork */
8813         if (element == EL_PLAYER_3 ||
8814             element == EL_PLAYER_4)
8815           continue;
8816
8817         if (g_em->bitmap != debug_bitmap ||
8818             g_em->src_x != debug_src_x ||
8819             g_em->src_y != debug_src_y)
8820         {
8821           static int last_i = -1;
8822
8823           if (i != last_i)
8824           {
8825             printf("\n");
8826             last_i = i;
8827           }
8828
8829           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8830                  p, i, element, element_info[element].token_name,
8831                  element_action_info[effective_action].suffix, direction);
8832
8833           if (element != effective_element)
8834             printf(" [%d ('%s')]",
8835                    effective_element,
8836                    element_info[effective_element].token_name);
8837
8838           printf("\n");
8839
8840           if (g_em->bitmap != debug_bitmap)
8841             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
8842                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
8843
8844           if (g_em->src_x != debug_src_x ||
8845               g_em->src_y != debug_src_y)
8846             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8847                    j,
8848                    g_em->src_x, g_em->src_y,
8849                    g_em->src_x / 32, g_em->src_y / 32,
8850                    debug_src_x, debug_src_y,
8851                    debug_src_x / 32, debug_src_y / 32);
8852
8853           num_em_gfx_errors++;
8854         }
8855 #endif
8856
8857       }
8858     }
8859   }
8860
8861 #if DEBUG_EM_GFX
8862   printf("\n");
8863   printf("::: [%d errors found]\n", num_em_gfx_errors);
8864
8865   exit(0);
8866 #endif
8867 }
8868
8869 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8870                                 boolean any_player_moving,
8871                                 boolean any_player_snapping,
8872                                 boolean any_player_dropping)
8873 {
8874   if (frame == 0 && !any_player_dropping)
8875   {
8876     if (!local_player->was_waiting)
8877     {
8878       if (!CheckSaveEngineSnapshotToList())
8879         return;
8880
8881       local_player->was_waiting = TRUE;
8882     }
8883   }
8884   else if (any_player_moving || any_player_snapping || any_player_dropping)
8885   {
8886     local_player->was_waiting = FALSE;
8887   }
8888 }
8889
8890 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8891                                 boolean murphy_is_dropping)
8892 {
8893   if (murphy_is_waiting)
8894   {
8895     if (!local_player->was_waiting)
8896     {
8897       if (!CheckSaveEngineSnapshotToList())
8898         return;
8899
8900       local_player->was_waiting = TRUE;
8901     }
8902   }
8903   else
8904   {
8905     local_player->was_waiting = FALSE;
8906   }
8907 }
8908
8909 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
8910                                 boolean button_released)
8911 {
8912   if (button_released)
8913   {
8914     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
8915       CheckSaveEngineSnapshotToList();
8916   }
8917   else if (element_clicked)
8918   {
8919     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
8920       CheckSaveEngineSnapshotToList();
8921
8922     game.snapshot.changed_action = TRUE;
8923   }
8924 }
8925
8926 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8927                             boolean any_player_moving,
8928                             boolean any_player_snapping,
8929                             boolean any_player_dropping)
8930 {
8931   if (tape.single_step && tape.recording && !tape.pausing)
8932     if (frame == 0 && !any_player_dropping)
8933       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8934
8935   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8936                              any_player_snapping, any_player_dropping);
8937 }
8938
8939 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8940                             boolean murphy_is_dropping)
8941 {
8942   boolean murphy_starts_dropping = FALSE;
8943   int i;
8944
8945   for (i = 0; i < MAX_PLAYERS; i++)
8946     if (stored_player[i].force_dropping)
8947       murphy_starts_dropping = TRUE;
8948
8949   if (tape.single_step && tape.recording && !tape.pausing)
8950     if (murphy_is_waiting && !murphy_starts_dropping)
8951       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8952
8953   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8954 }
8955
8956 void CheckSingleStepMode_MM(boolean element_clicked,
8957                             boolean button_released)
8958 {
8959   if (tape.single_step && tape.recording && !tape.pausing)
8960     if (button_released)
8961       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8962
8963   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
8964 }
8965
8966 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8967                          int graphic, int sync_frame, int x, int y)
8968 {
8969   int frame = getGraphicAnimationFrame(graphic, sync_frame);
8970
8971   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8972 }
8973
8974 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8975 {
8976   return (IS_NEXT_FRAME(sync_frame, graphic));
8977 }
8978
8979 int getGraphicInfo_Delay(int graphic)
8980 {
8981   return graphic_info[graphic].anim_delay;
8982 }
8983
8984 void PlayMenuSoundExt(int sound)
8985 {
8986   if (sound == SND_UNDEFINED)
8987     return;
8988
8989   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8990       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8991     return;
8992
8993   if (IS_LOOP_SOUND(sound))
8994     PlaySoundLoop(sound);
8995   else
8996     PlaySound(sound);
8997 }
8998
8999 void PlayMenuSound()
9000 {
9001   PlayMenuSoundExt(menu.sound[game_status]);
9002 }
9003
9004 void PlayMenuSoundStereo(int sound, int stereo_position)
9005 {
9006   if (sound == SND_UNDEFINED)
9007     return;
9008
9009   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9010       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9011     return;
9012
9013   if (IS_LOOP_SOUND(sound))
9014     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9015   else
9016     PlaySoundStereo(sound, stereo_position);
9017 }
9018
9019 void PlayMenuSoundIfLoopExt(int sound)
9020 {
9021   if (sound == SND_UNDEFINED)
9022     return;
9023
9024   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9025       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9026     return;
9027
9028   if (IS_LOOP_SOUND(sound))
9029     PlaySoundLoop(sound);
9030 }
9031
9032 void PlayMenuSoundIfLoop()
9033 {
9034   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9035 }
9036
9037 void PlayMenuMusicExt(int music)
9038 {
9039   if (music == MUS_UNDEFINED)
9040     return;
9041
9042   if (!setup.sound_music)
9043     return;
9044
9045   PlayMusic(music);
9046 }
9047
9048 void PlayMenuMusic()
9049 {
9050   char *curr_music = getCurrentlyPlayingMusicFilename();
9051   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9052
9053   if (!strEqual(curr_music, next_music))
9054     PlayMenuMusicExt(menu.music[game_status]);
9055 }
9056
9057 void PlayMenuSoundsAndMusic()
9058 {
9059   PlayMenuSound();
9060   PlayMenuMusic();
9061 }
9062
9063 static void FadeMenuSounds()
9064 {
9065   FadeSounds();
9066 }
9067
9068 static void FadeMenuMusic()
9069 {
9070   char *curr_music = getCurrentlyPlayingMusicFilename();
9071   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9072
9073   if (!strEqual(curr_music, next_music))
9074     FadeMusic();
9075 }
9076
9077 void FadeMenuSoundsAndMusic()
9078 {
9079   FadeMenuSounds();
9080   FadeMenuMusic();
9081 }
9082
9083 void PlaySoundActivating()
9084 {
9085 #if 0
9086   PlaySound(SND_MENU_ITEM_ACTIVATING);
9087 #endif
9088 }
9089
9090 void PlaySoundSelecting()
9091 {
9092 #if 0
9093   PlaySound(SND_MENU_ITEM_SELECTING);
9094 #endif
9095 }
9096
9097 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
9098 {
9099   boolean change_fullscreen = (setup.fullscreen !=
9100                                video.fullscreen_enabled);
9101   boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9102                                            setup.window_scaling_percent !=
9103                                            video.window_scaling_percent);
9104
9105   if (change_window_scaling_percent && video.fullscreen_enabled)
9106     return;
9107
9108   if (!change_window_scaling_percent && !video.fullscreen_available)
9109     return;
9110
9111 #if defined(TARGET_SDL2)
9112   if (change_window_scaling_percent)
9113   {
9114     SDLSetWindowScaling(setup.window_scaling_percent);
9115
9116     return;
9117   }
9118   else if (change_fullscreen)
9119   {
9120     SDLSetWindowFullscreen(setup.fullscreen);
9121
9122     /* set setup value according to successfully changed fullscreen mode */
9123     setup.fullscreen = video.fullscreen_enabled;
9124
9125     return;
9126   }
9127 #endif
9128
9129   if (change_fullscreen ||
9130       change_window_scaling_percent)
9131   {
9132     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9133
9134     /* save backbuffer content which gets lost when toggling fullscreen mode */
9135     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9136
9137     if (change_window_scaling_percent)
9138     {
9139       /* keep window mode, but change window scaling */
9140       video.fullscreen_enabled = TRUE;          /* force new window scaling */
9141     }
9142
9143     /* toggle fullscreen */
9144     ChangeVideoModeIfNeeded(setup.fullscreen);
9145
9146     /* set setup value according to successfully changed fullscreen mode */
9147     setup.fullscreen = video.fullscreen_enabled;
9148
9149     /* restore backbuffer content from temporary backbuffer backup bitmap */
9150     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9151
9152     FreeBitmap(tmp_backbuffer);
9153
9154     /* update visible window/screen */
9155     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9156   }
9157 }
9158
9159 void JoinRectangles(int *x, int *y, int *width, int *height,
9160                     int x2, int y2, int width2, int height2)
9161 {
9162   // do not join with "off-screen" rectangle
9163   if (x2 == -1 || y2 == -1)
9164     return;
9165
9166   *x = MIN(*x, x2);
9167   *y = MIN(*y, y2);
9168   *width = MAX(*width, width2);
9169   *height = MAX(*height, height2);
9170 }
9171
9172 void SetAnimStatus(int anim_status_new)
9173 {
9174   if (anim_status_new == GAME_MODE_MAIN)
9175     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9176   else if (anim_status_new == GAME_MODE_SCORES)
9177     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9178
9179   global.anim_status_next = anim_status_new;
9180
9181   // directly set screen modes that are entered without fading
9182   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
9183        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9184       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
9185        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9186     global.anim_status = global.anim_status_next;
9187 }
9188
9189 void SetGameStatus(int game_status_new)
9190 {
9191   if (game_status_new != game_status)
9192     game_status_last_screen = game_status;
9193
9194   game_status = game_status_new;
9195
9196   SetAnimStatus(game_status_new);
9197 }
9198
9199 void SetFontStatus(int game_status_new)
9200 {
9201   static int last_game_status = -1;
9202
9203   if (game_status_new != -1)
9204   {
9205     // set game status for font use after storing last game status
9206     last_game_status = game_status;
9207     game_status = game_status_new;
9208   }
9209   else
9210   {
9211     // reset game status after font use from last stored game status
9212     game_status = last_game_status;
9213   }
9214 }
9215
9216 void ResetFontStatus()
9217 {
9218   SetFontStatus(-1);
9219 }
9220
9221 boolean CheckIfPlayfieldViewportHasChanged()
9222 {
9223   // if game status has not changed, playfield viewport has not changed either
9224   if (game_status == game_status_last)
9225     return FALSE;
9226
9227   // check if playfield viewport has changed with current game status
9228   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9229   int new_real_sx       = vp_playfield->x;
9230   int new_real_sy       = vp_playfield->y;
9231   int new_full_sxsize   = vp_playfield->width;
9232   int new_full_sysize   = vp_playfield->height;
9233
9234   return (new_real_sx != REAL_SX ||
9235           new_real_sy != REAL_SY ||
9236           new_full_sxsize != FULL_SXSIZE ||
9237           new_full_sysize != FULL_SYSIZE);
9238 }
9239
9240 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9241 {
9242   return (CheckIfGlobalBorderHasChanged() ||
9243           CheckIfPlayfieldViewportHasChanged());
9244 }
9245
9246 void ChangeViewportPropertiesIfNeeded()
9247 {
9248   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9249                                FALSE : setup.small_game_graphics);
9250   int gfx_game_mode = game_status;
9251   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9252                         game_status);
9253   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
9254   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9255   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
9256   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
9257   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
9258   int new_win_xsize     = vp_window->width;
9259   int new_win_ysize     = vp_window->height;
9260   int border_size       = vp_playfield->border_size;
9261   int new_sx            = vp_playfield->x + border_size;
9262   int new_sy            = vp_playfield->y + border_size;
9263   int new_sxsize        = vp_playfield->width  - 2 * border_size;
9264   int new_sysize        = vp_playfield->height - 2 * border_size;
9265   int new_real_sx       = vp_playfield->x;
9266   int new_real_sy       = vp_playfield->y;
9267   int new_full_sxsize   = vp_playfield->width;
9268   int new_full_sysize   = vp_playfield->height;
9269   int new_dx            = vp_door_1->x;
9270   int new_dy            = vp_door_1->y;
9271   int new_dxsize        = vp_door_1->width;
9272   int new_dysize        = vp_door_1->height;
9273   int new_vx            = vp_door_2->x;
9274   int new_vy            = vp_door_2->y;
9275   int new_vxsize        = vp_door_2->width;
9276   int new_vysize        = vp_door_2->height;
9277   int new_ex            = vp_door_3->x;
9278   int new_ey            = vp_door_3->y;
9279   int new_exsize        = vp_door_3->width;
9280   int new_eysize        = vp_door_3->height;
9281   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9282   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9283                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9284   int new_scr_fieldx = new_sxsize / tilesize;
9285   int new_scr_fieldy = new_sysize / tilesize;
9286   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9287   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9288   boolean init_gfx_buffers = FALSE;
9289   boolean init_video_buffer = FALSE;
9290   boolean init_gadgets_and_anims = FALSE;
9291   boolean init_em_graphics = FALSE;
9292
9293   if (new_win_xsize != WIN_XSIZE ||
9294       new_win_ysize != WIN_YSIZE)
9295   {
9296     WIN_XSIZE = new_win_xsize;
9297     WIN_YSIZE = new_win_ysize;
9298
9299     init_video_buffer = TRUE;
9300     init_gfx_buffers = TRUE;
9301     init_gadgets_and_anims = TRUE;
9302
9303     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9304   }
9305
9306   if (new_scr_fieldx != SCR_FIELDX ||
9307       new_scr_fieldy != SCR_FIELDY)
9308   {
9309     /* this always toggles between MAIN and GAME when using small tile size */
9310
9311     SCR_FIELDX = new_scr_fieldx;
9312     SCR_FIELDY = new_scr_fieldy;
9313
9314     // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9315   }
9316
9317   if (new_sx != SX ||
9318       new_sy != SY ||
9319       new_dx != DX ||
9320       new_dy != DY ||
9321       new_vx != VX ||
9322       new_vy != VY ||
9323       new_ex != EX ||
9324       new_ey != EY ||
9325       new_sxsize != SXSIZE ||
9326       new_sysize != SYSIZE ||
9327       new_dxsize != DXSIZE ||
9328       new_dysize != DYSIZE ||
9329       new_vxsize != VXSIZE ||
9330       new_vysize != VYSIZE ||
9331       new_exsize != EXSIZE ||
9332       new_eysize != EYSIZE ||
9333       new_real_sx != REAL_SX ||
9334       new_real_sy != REAL_SY ||
9335       new_full_sxsize != FULL_SXSIZE ||
9336       new_full_sysize != FULL_SYSIZE ||
9337       new_tilesize_var != TILESIZE_VAR
9338       )
9339   {
9340     // ------------------------------------------------------------------------
9341     // determine next fading area for changed viewport definitions
9342     // ------------------------------------------------------------------------
9343
9344     // start with current playfield area (default fading area)
9345     FADE_SX = REAL_SX;
9346     FADE_SY = REAL_SY;
9347     FADE_SXSIZE = FULL_SXSIZE;
9348     FADE_SYSIZE = FULL_SYSIZE;
9349
9350     // add new playfield area if position or size has changed
9351     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9352         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9353     {
9354       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9355                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9356     }
9357
9358     // add current and new door 1 area if position or size has changed
9359     if (new_dx != DX || new_dy != DY ||
9360         new_dxsize != DXSIZE || new_dysize != DYSIZE)
9361     {
9362       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9363                      DX, DY, DXSIZE, DYSIZE);
9364       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9365                      new_dx, new_dy, new_dxsize, new_dysize);
9366     }
9367
9368     // add current and new door 2 area if position or size has changed
9369     if (new_dx != VX || new_dy != VY ||
9370         new_dxsize != VXSIZE || new_dysize != VYSIZE)
9371     {
9372       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9373                      VX, VY, VXSIZE, VYSIZE);
9374       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9375                      new_vx, new_vy, new_vxsize, new_vysize);
9376     }
9377
9378     // ------------------------------------------------------------------------
9379     // handle changed tile size
9380     // ------------------------------------------------------------------------
9381
9382     if (new_tilesize_var != TILESIZE_VAR)
9383     {
9384       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9385
9386       // changing tile size invalidates scroll values of engine snapshots
9387       FreeEngineSnapshotSingle();
9388
9389       // changing tile size requires update of graphic mapping for EM engine
9390       init_em_graphics = TRUE;
9391     }
9392
9393     SX = new_sx;
9394     SY = new_sy;
9395     DX = new_dx;
9396     DY = new_dy;
9397     VX = new_vx;
9398     VY = new_vy;
9399     EX = new_ex;
9400     EY = new_ey;
9401     SXSIZE = new_sxsize;
9402     SYSIZE = new_sysize;
9403     DXSIZE = new_dxsize;
9404     DYSIZE = new_dysize;
9405     VXSIZE = new_vxsize;
9406     VYSIZE = new_vysize;
9407     EXSIZE = new_exsize;
9408     EYSIZE = new_eysize;
9409     REAL_SX = new_real_sx;
9410     REAL_SY = new_real_sy;
9411     FULL_SXSIZE = new_full_sxsize;
9412     FULL_SYSIZE = new_full_sysize;
9413     TILESIZE_VAR = new_tilesize_var;
9414
9415     init_gfx_buffers = TRUE;
9416     init_gadgets_and_anims = TRUE;
9417
9418     // printf("::: viewports: init_gfx_buffers\n");
9419     // printf("::: viewports: init_gadgets_and_anims\n");
9420   }
9421
9422   if (init_gfx_buffers)
9423   {
9424     // printf("::: init_gfx_buffers\n");
9425
9426     SCR_FIELDX = new_scr_fieldx_buffers;
9427     SCR_FIELDY = new_scr_fieldy_buffers;
9428
9429     InitGfxBuffers();
9430
9431     SCR_FIELDX = new_scr_fieldx;
9432     SCR_FIELDY = new_scr_fieldy;
9433
9434     SetDrawDeactivationMask(REDRAW_NONE);
9435     SetDrawBackgroundMask(REDRAW_FIELD);
9436   }
9437
9438   if (init_video_buffer)
9439   {
9440     // printf("::: init_video_buffer\n");
9441
9442     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9443     InitImageTextures();
9444   }
9445
9446   if (init_gadgets_and_anims)
9447   {
9448     // printf("::: init_gadgets_and_anims\n");
9449
9450     InitGadgets();
9451     InitGlobalAnimations();
9452   }
9453
9454   if (init_em_graphics)
9455   {
9456       InitGraphicInfo_EM();
9457   }
9458 }