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