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