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