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