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