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