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