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