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