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