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