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