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