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