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