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