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