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