added dynamically calculating text properties for door 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   // set envelope request run-time values
3060   request.sx = sx;
3061   request.sy = sy;
3062   request.xsize = xsize;
3063   request.ysize = ysize;
3064 }
3065
3066 void DrawEnvelopeRequestToScreen(int drawing_target)
3067 {
3068   if (global.use_envelope_request &&
3069       game.request_active &&
3070       drawing_target == DRAW_TO_SCREEN)
3071   {
3072     if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3073       BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3074                          request.sx, request.sy);
3075     else
3076       BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3077                    request.sx, request.sy);
3078   }
3079 }
3080
3081 static void setRequestBasePosition(int *x, int *y)
3082 {
3083   int sx_base, sy_base;
3084
3085   if (request.x != -1)
3086     sx_base = request.x;
3087   else if (request.align == ALIGN_LEFT)
3088     sx_base = SX;
3089   else if (request.align == ALIGN_RIGHT)
3090     sx_base = SX + SXSIZE;
3091   else
3092     sx_base = SX + SXSIZE / 2;
3093
3094   if (request.y != -1)
3095     sy_base = request.y;
3096   else if (request.valign == VALIGN_TOP)
3097     sy_base = SY;
3098   else if (request.valign == VALIGN_BOTTOM)
3099     sy_base = SY + SYSIZE;
3100   else
3101     sy_base = SY + SYSIZE / 2;
3102
3103   *x = sx_base;
3104   *y = sy_base;
3105 }
3106
3107 static void setRequestPositionExt(int *x, int *y, int width, int height,
3108                                   boolean add_border_size)
3109 {
3110   int border_size = request.border_size;
3111   int sx_base, sy_base;
3112   int sx, sy;
3113
3114   setRequestBasePosition(&sx_base, &sy_base);
3115
3116   if (request.align == ALIGN_LEFT)
3117     sx = sx_base;
3118   else if (request.align == ALIGN_RIGHT)
3119     sx = sx_base - width;
3120   else
3121     sx = sx_base - width  / 2;
3122
3123   if (request.valign == VALIGN_TOP)
3124     sy = sy_base;
3125   else if (request.valign == VALIGN_BOTTOM)
3126     sy = sy_base - height;
3127   else
3128     sy = sy_base - height / 2;
3129
3130   sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3131   sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3132
3133   if (add_border_size)
3134   {
3135     sx += border_size;
3136     sy += border_size;
3137   }
3138
3139   *x = sx;
3140   *y = sy;
3141 }
3142
3143 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3144 {
3145   setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3146 }
3147
3148 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3149 {
3150   DrawBuffer *drawto_last = drawto;
3151   char *text_final = text;
3152   char *text_door_style = NULL;
3153   int graphic = IMG_BACKGROUND_REQUEST;
3154   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3155   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3156   int font_nr = FONT_REQUEST;
3157   int font_width = getFontWidth(font_nr);
3158   int font_height = getFontHeight(font_nr);
3159   int border_size = request.border_size;
3160   int line_spacing = request.line_spacing;
3161   int line_height = font_height + line_spacing;
3162   int max_text_width  = request.width  - 2 * border_size;
3163   int max_text_height = request.height - 2 * border_size;
3164   int line_length = max_text_width  / font_width;
3165   int max_lines   = max_text_height / line_height;
3166   int text_width = line_length * font_width;
3167   int width = request.width;
3168   int height = request.height;
3169   int tile_size = MAX(request.step_offset, 1);
3170   int x_steps = width  / tile_size;
3171   int y_steps = height / tile_size;
3172   int sx_offset = border_size;
3173   int sy_offset = border_size;
3174   int sx, sy;
3175   int x, y;
3176
3177   if (request.centered)
3178     sx_offset = (request.width - text_width) / 2;
3179
3180   if (request.wrap_single_words && !request.autowrap)
3181   {
3182     char *src_text_ptr, *dst_text_ptr;
3183
3184     text_door_style = checked_malloc(2 * strlen(text) + 1);
3185
3186     src_text_ptr = text;
3187     dst_text_ptr = text_door_style;
3188
3189     while (*src_text_ptr)
3190     {
3191       if (*src_text_ptr == ' ' ||
3192           *src_text_ptr == '?' ||
3193           *src_text_ptr == '!')
3194         *dst_text_ptr++ = '\n';
3195
3196       if (*src_text_ptr != ' ')
3197         *dst_text_ptr++ = *src_text_ptr;
3198
3199       src_text_ptr++;
3200     }
3201
3202     *dst_text_ptr = '\0';
3203
3204     text_final = text_door_style;
3205   }
3206
3207   setRequestPosition(&sx, &sy, FALSE);
3208
3209   // draw complete envelope request to temporary bitmap
3210   drawto = bitmap_db_store_1;
3211
3212   ClearRectangle(drawto, sx, sy, width, height);
3213
3214   for (y = 0; y < y_steps; y++)
3215     for (x = 0; x < x_steps; x++)
3216       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3217                                   x, y, x_steps, y_steps,
3218                                   tile_size, tile_size);
3219
3220   // force DOOR font inside door area
3221   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3222
3223   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3224                  line_length, -1, max_lines, line_spacing, mask_mode,
3225                  request.autowrap, request.centered, FALSE);
3226
3227   ResetFontStatus();
3228
3229   MapToolButtons(req_state);
3230
3231   // restore pointer to drawing buffer
3232   drawto = drawto_last;
3233
3234   // prepare complete envelope request from temporary bitmap
3235   PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3236
3237   if (text_door_style)
3238     free(text_door_style);
3239 }
3240
3241 static void AnimateEnvelopeRequest(int anim_mode, int action)
3242 {
3243   boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3244   int delay_value_normal = request.step_delay;
3245   int delay_value_fast = delay_value_normal / 2;
3246   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3247   boolean no_delay = (tape.warp_forward);
3248   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3249   int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3250   DelayCounter anim_delay = { anim_delay_value };
3251
3252   int tile_size = MAX(request.step_offset, 1);
3253   int max_xsize = request.width  / tile_size;
3254   int max_ysize = request.height / tile_size;
3255   int max_xsize_inner = max_xsize - 2;
3256   int max_ysize_inner = max_ysize - 2;
3257
3258   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3259   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3260   int xend = max_xsize_inner;
3261   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3262   int xstep = (xstart < xend ? 1 : 0);
3263   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3264   int start = 0;
3265   int end = MAX(xend - xstart, yend - ystart);
3266   int i;
3267
3268   if (setup.quick_doors)
3269   {
3270     xstart = xend;
3271     ystart = yend;
3272     end = 0;
3273   }
3274
3275   for (i = start; i <= end; i++)
3276   {
3277     int last_frame = end;       // last frame of this "for" loop
3278     int x = xstart + i * xstep;
3279     int y = ystart + i * ystep;
3280     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3281     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3282     int xsize_size_left = (xsize - 1) * tile_size;
3283     int ysize_size_top  = (ysize - 1) * tile_size;
3284     int max_xsize_pos = (max_xsize - 1) * tile_size;
3285     int max_ysize_pos = (max_ysize - 1) * tile_size;
3286     int width  = xsize * tile_size;
3287     int height = ysize * tile_size;
3288     int src_x, src_y;
3289     int dst_x, dst_y;
3290     int xx, yy;
3291
3292     if (game_ended)
3293       HandleGameActions();
3294
3295     setRequestPosition(&src_x, &src_y, FALSE);
3296     setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3297
3298     for (yy = 0; yy < 2; yy++)
3299     {
3300       for (xx = 0; xx < 2; xx++)
3301       {
3302         int src_xx = src_x + xx * max_xsize_pos;
3303         int src_yy = src_y + yy * max_ysize_pos;
3304         int dst_xx = dst_x + xx * xsize_size_left;
3305         int dst_yy = dst_y + yy * ysize_size_top;
3306         int xx_size = (xx ? tile_size : xsize_size_left);
3307         int yy_size = (yy ? tile_size : ysize_size_top);
3308
3309         // draw partial (animated) envelope request to temporary bitmap
3310         BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3311                    src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3312       }
3313     }
3314
3315     // prepare partial (animated) envelope request from temporary bitmap
3316     PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3317                                    width, height);
3318
3319     redraw_mask |= REDRAW_FIELD;
3320
3321     BackToFront();
3322
3323     SkipUntilDelayReached(&anim_delay, &i, last_frame);
3324   }
3325
3326   ClearAutoRepeatKeyEvents();
3327 }
3328
3329 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3330 {
3331   int graphic = IMG_BACKGROUND_REQUEST;
3332   int sound_opening = SND_REQUEST_OPENING;
3333   int sound_closing = SND_REQUEST_CLOSING;
3334   int anim_mode_1 = request.anim_mode;                  // (higher priority)
3335   int anim_mode_2 = graphic_info[graphic].anim_mode;    // (lower priority)
3336   int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3337   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3338                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3339
3340   game.envelope_active = TRUE;  // needed for RedrawPlayfield() events
3341
3342   if (action == ACTION_OPENING)
3343   {
3344     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3345
3346     if (anim_mode == ANIM_DEFAULT)
3347       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3348
3349     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3350   }
3351   else
3352   {
3353     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3354
3355     if (anim_mode != ANIM_NONE)
3356       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3357
3358     if (anim_mode == ANIM_DEFAULT)
3359       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3360   }
3361
3362   game.envelope_active = FALSE;
3363 }
3364
3365 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3366 {
3367   if (IS_MM_WALL(element))
3368   {
3369     DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3370   }
3371   else
3372   {
3373     Bitmap *src_bitmap;
3374     int src_x, src_y;
3375     int graphic = el2preimg(element);
3376
3377     getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3378     BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3379                dst_x, dst_y);
3380   }
3381 }
3382
3383 void DrawLevel(int draw_background_mask)
3384 {
3385   int x, y;
3386
3387   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3388   SetDrawBackgroundMask(draw_background_mask);
3389
3390   ClearField();
3391
3392   for (x = BX1; x <= BX2; x++)
3393     for (y = BY1; y <= BY2; y++)
3394       DrawScreenField(x, y);
3395
3396   redraw_mask |= REDRAW_FIELD;
3397 }
3398
3399 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3400                     int tilesize)
3401 {
3402   int x, y;
3403
3404   for (x = 0; x < size_x; x++)
3405     for (y = 0; y < size_y; y++)
3406       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3407
3408   redraw_mask |= REDRAW_FIELD;
3409 }
3410
3411 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3412 {
3413   int x, y;
3414
3415   for (x = 0; x < size_x; x++)
3416     for (y = 0; y < size_y; y++)
3417       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3418
3419   redraw_mask |= REDRAW_FIELD;
3420 }
3421
3422 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3423 {
3424   boolean show_level_border = (BorderElement != EL_EMPTY);
3425   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3426   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3427   int tile_size = preview.tile_size;
3428   int preview_width  = preview.xsize * tile_size;
3429   int preview_height = preview.ysize * tile_size;
3430   int real_preview_xsize = MIN(level_xsize, preview.xsize);
3431   int real_preview_ysize = MIN(level_ysize, preview.ysize);
3432   int real_preview_width  = real_preview_xsize * tile_size;
3433   int real_preview_height = real_preview_ysize * tile_size;
3434   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3435   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3436   int x, y;
3437
3438   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3439     return;
3440
3441   DrawBackground(dst_x, dst_y, preview_width, preview_height);
3442
3443   dst_x += (preview_width  - real_preview_width)  / 2;
3444   dst_y += (preview_height - real_preview_height) / 2;
3445
3446   for (x = 0; x < real_preview_xsize; x++)
3447   {
3448     for (y = 0; y < real_preview_ysize; y++)
3449     {
3450       int lx = from_x + x + (show_level_border ? -1 : 0);
3451       int ly = from_y + y + (show_level_border ? -1 : 0);
3452       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3453                      getBorderElement(lx, ly));
3454
3455       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3456                          element, tile_size);
3457     }
3458   }
3459
3460   redraw_mask |= REDRAW_FIELD;
3461 }
3462
3463 #define MICROLABEL_EMPTY                0
3464 #define MICROLABEL_LEVEL_NAME           1
3465 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
3466 #define MICROLABEL_LEVEL_AUTHOR         3
3467 #define MICROLABEL_IMPORTED_FROM_HEAD   4
3468 #define MICROLABEL_IMPORTED_FROM        5
3469 #define MICROLABEL_IMPORTED_BY_HEAD     6
3470 #define MICROLABEL_IMPORTED_BY          7
3471
3472 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3473 {
3474   int max_text_width = SXSIZE;
3475   int font_width = getFontWidth(font_nr);
3476
3477   if (pos->align == ALIGN_CENTER)
3478     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3479   else if (pos->align == ALIGN_RIGHT)
3480     max_text_width = pos->x;
3481   else
3482     max_text_width = SXSIZE - pos->x;
3483
3484   return max_text_width / font_width;
3485 }
3486
3487 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3488 {
3489   char label_text[MAX_OUTPUT_LINESIZE + 1];
3490   int max_len_label_text;
3491   int font_nr = pos->font;
3492   int i;
3493
3494   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3495     return;
3496
3497   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3498       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3499       mode == MICROLABEL_IMPORTED_BY_HEAD)
3500     font_nr = pos->font_alt;
3501
3502   max_len_label_text = getMaxTextLength(pos, font_nr);
3503
3504   if (pos->size != -1)
3505     max_len_label_text = pos->size;
3506
3507   for (i = 0; i < max_len_label_text; i++)
3508     label_text[i] = ' ';
3509   label_text[max_len_label_text] = '\0';
3510
3511   if (strlen(label_text) > 0)
3512     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3513
3514   strncpy(label_text,
3515           (mode == MICROLABEL_LEVEL_NAME ? level.name :
3516            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3517            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3518            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3519            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3520            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3521            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3522           max_len_label_text);
3523   label_text[max_len_label_text] = '\0';
3524
3525   if (strlen(label_text) > 0)
3526     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3527
3528   redraw_mask |= REDRAW_FIELD;
3529 }
3530
3531 static void DrawPreviewLevelLabel(int mode)
3532 {
3533   DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3534 }
3535
3536 static void DrawPreviewLevelInfo(int mode)
3537 {
3538   if (mode == MICROLABEL_LEVEL_NAME)
3539     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3540   else if (mode == MICROLABEL_LEVEL_AUTHOR)
3541     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3542 }
3543
3544 static void DrawPreviewLevelExt(boolean restart)
3545 {
3546   static DelayCounter scroll_delay = { 0 };
3547   static DelayCounter label_delay = { 0 };
3548   static int from_x, from_y, scroll_direction;
3549   static int label_state, label_counter;
3550   boolean show_level_border = (BorderElement != EL_EMPTY);
3551   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3552   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3553
3554   scroll_delay.value = preview.step_delay;
3555   label_delay.value = MICROLEVEL_LABEL_DELAY;
3556
3557   if (restart)
3558   {
3559     from_x = 0;
3560     from_y = 0;
3561
3562     if (preview.anim_mode == ANIM_CENTERED)
3563     {
3564       if (level_xsize > preview.xsize)
3565         from_x = (level_xsize - preview.xsize) / 2;
3566       if (level_ysize > preview.ysize)
3567         from_y = (level_ysize - preview.ysize) / 2;
3568     }
3569
3570     from_x += preview.xoffset;
3571     from_y += preview.yoffset;
3572
3573     scroll_direction = MV_RIGHT;
3574     label_state = 1;
3575     label_counter = 0;
3576
3577     DrawPreviewLevelPlayfield(from_x, from_y);
3578     DrawPreviewLevelLabel(label_state);
3579
3580     DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3581     DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3582
3583     // initialize delay counters
3584     ResetDelayCounter(&scroll_delay);
3585     ResetDelayCounter(&label_delay);
3586
3587     if (leveldir_current->name)
3588     {
3589       struct TextPosInfo *pos = &menu.main.text.level_info_1;
3590       char label_text[MAX_OUTPUT_LINESIZE + 1];
3591       int font_nr = pos->font;
3592       int max_len_label_text = getMaxTextLength(pos, font_nr);
3593
3594       if (pos->size != -1)
3595         max_len_label_text = pos->size;
3596
3597       strncpy(label_text, leveldir_current->name, max_len_label_text);
3598       label_text[max_len_label_text] = '\0';
3599
3600       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3601         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3602     }
3603
3604     return;
3605   }
3606
3607   // scroll preview level, if needed
3608   if (preview.anim_mode != ANIM_NONE &&
3609       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3610       DelayReached(&scroll_delay))
3611   {
3612     switch (scroll_direction)
3613     {
3614       case MV_LEFT:
3615         if (from_x > 0)
3616         {
3617           from_x -= preview.step_offset;
3618           from_x = (from_x < 0 ? 0 : from_x);
3619         }
3620         else
3621           scroll_direction = MV_UP;
3622         break;
3623
3624       case MV_RIGHT:
3625         if (from_x < level_xsize - preview.xsize)
3626         {
3627           from_x += preview.step_offset;
3628           from_x = (from_x > level_xsize - preview.xsize ?
3629                     level_xsize - preview.xsize : from_x);
3630         }
3631         else
3632           scroll_direction = MV_DOWN;
3633         break;
3634
3635       case MV_UP:
3636         if (from_y > 0)
3637         {
3638           from_y -= preview.step_offset;
3639           from_y = (from_y < 0 ? 0 : from_y);
3640         }
3641         else
3642           scroll_direction = MV_RIGHT;
3643         break;
3644
3645       case MV_DOWN:
3646         if (from_y < level_ysize - preview.ysize)
3647         {
3648           from_y += preview.step_offset;
3649           from_y = (from_y > level_ysize - preview.ysize ?
3650                     level_ysize - preview.ysize : from_y);
3651         }
3652         else
3653           scroll_direction = MV_LEFT;
3654         break;
3655
3656       default:
3657         break;
3658     }
3659
3660     DrawPreviewLevelPlayfield(from_x, from_y);
3661   }
3662
3663   // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3664   // redraw micro level label, if needed
3665   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3666       !strEqual(level.author, ANONYMOUS_NAME) &&
3667       !strEqual(level.author, leveldir_current->name) &&
3668       DelayReached(&label_delay))
3669   {
3670     int max_label_counter = 23;
3671
3672     if (leveldir_current->imported_from != NULL &&
3673         strlen(leveldir_current->imported_from) > 0)
3674       max_label_counter += 14;
3675     if (leveldir_current->imported_by != NULL &&
3676         strlen(leveldir_current->imported_by) > 0)
3677       max_label_counter += 14;
3678
3679     label_counter = (label_counter + 1) % max_label_counter;
3680     label_state = (label_counter >= 0 && label_counter <= 7 ?
3681                    MICROLABEL_LEVEL_NAME :
3682                    label_counter >= 9 && label_counter <= 12 ?
3683                    MICROLABEL_LEVEL_AUTHOR_HEAD :
3684                    label_counter >= 14 && label_counter <= 21 ?
3685                    MICROLABEL_LEVEL_AUTHOR :
3686                    label_counter >= 23 && label_counter <= 26 ?
3687                    MICROLABEL_IMPORTED_FROM_HEAD :
3688                    label_counter >= 28 && label_counter <= 35 ?
3689                    MICROLABEL_IMPORTED_FROM :
3690                    label_counter >= 37 && label_counter <= 40 ?
3691                    MICROLABEL_IMPORTED_BY_HEAD :
3692                    label_counter >= 42 && label_counter <= 49 ?
3693                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3694
3695     if (leveldir_current->imported_from == NULL &&
3696         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3697          label_state == MICROLABEL_IMPORTED_FROM))
3698       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3699                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3700
3701     DrawPreviewLevelLabel(label_state);
3702   }
3703 }
3704
3705 void DrawPreviewPlayers(void)
3706 {
3707   if (game_status != GAME_MODE_MAIN)
3708     return;
3709
3710   // do not draw preview players if level preview redefined, but players aren't
3711   if (preview.redefined && !menu.main.preview_players.redefined)
3712     return;
3713
3714   boolean player_found[MAX_PLAYERS];
3715   int num_players = 0;
3716   int i, x, y;
3717
3718   for (i = 0; i < MAX_PLAYERS; i++)
3719     player_found[i] = FALSE;
3720
3721   // check which players can be found in the level (simple approach)
3722   for (x = 0; x < lev_fieldx; x++)
3723   {
3724     for (y = 0; y < lev_fieldy; y++)
3725     {
3726       int element = level.field[x][y];
3727
3728       if (IS_PLAYER_ELEMENT(element))
3729       {
3730         int player_nr = GET_PLAYER_NR(element);
3731
3732         player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3733
3734         if (!player_found[player_nr])
3735           num_players++;
3736
3737         player_found[player_nr] = TRUE;
3738       }
3739     }
3740   }
3741
3742   struct TextPosInfo *pos = &menu.main.preview_players;
3743   int tile_size = pos->tile_size;
3744   int border_size = pos->border_size;
3745   int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3746   int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3747   int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3748   int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3749   int max_players_width  = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3750   int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3751   int all_players_width  = (num_players - 1) * player_xoffset + tile_size;
3752   int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3753   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3754   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3755   int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width,  pos->align);
3756   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3757
3758   // clear area in which the players will be drawn
3759   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3760                              max_players_width, max_players_height);
3761
3762   if (!network.enabled && !setup.team_mode)
3763     return;
3764
3765   // only draw players if level is suited for team mode
3766   if (num_players < 2)
3767     return;
3768
3769   // draw all players that were found in the level
3770   for (i = 0; i < MAX_PLAYERS; i++)
3771   {
3772     if (player_found[i])
3773     {
3774       int graphic = el2img(EL_PLAYER_1 + i);
3775
3776       DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3777
3778       xpos += player_xoffset;
3779       ypos += player_yoffset;
3780     }
3781   }
3782 }
3783
3784 void DrawPreviewLevelInitial(void)
3785 {
3786   DrawPreviewLevelExt(TRUE);
3787   DrawPreviewPlayers();
3788 }
3789
3790 void DrawPreviewLevelAnimation(void)
3791 {
3792   DrawPreviewLevelExt(FALSE);
3793 }
3794
3795 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3796                               int border_size, int font_nr)
3797 {
3798   int graphic = el2img(EL_PLAYER_1 + player_nr);
3799   int font_height = getFontHeight(font_nr);
3800   int player_height = MAX(tile_size, font_height);
3801   int xoffset_text = tile_size + border_size;
3802   int yoffset_text    = (player_height - font_height) / 2;
3803   int yoffset_graphic = (player_height - tile_size) / 2;
3804   char *player_name = getNetworkPlayerName(player_nr + 1);
3805
3806   DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3807                               tile_size);
3808   DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3809 }
3810
3811 static void DrawNetworkPlayersExt(boolean force)
3812 {
3813   if (game_status != GAME_MODE_MAIN)
3814     return;
3815
3816   if (!network.connected && !force)
3817     return;
3818
3819   // do not draw network players if level preview redefined, but players aren't
3820   if (preview.redefined && !menu.main.network_players.redefined)
3821     return;
3822
3823   int num_players = 0;
3824   int i;
3825
3826   for (i = 0; i < MAX_PLAYERS; i++)
3827     if (stored_player[i].connected_network)
3828       num_players++;
3829
3830   struct TextPosInfo *pos = &menu.main.network_players;
3831   int tile_size = pos->tile_size;
3832   int border_size = pos->border_size;
3833   int xoffset_text = tile_size + border_size;
3834   int font_nr = pos->font;
3835   int font_width = getFontWidth(font_nr);
3836   int font_height = getFontHeight(font_nr);
3837   int player_height = MAX(tile_size, font_height);
3838   int player_yoffset = player_height + border_size;
3839   int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3840   int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3841   int all_players_height = num_players * player_yoffset - border_size;
3842   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3843   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3844   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3845
3846   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3847                              max_players_width, max_players_height);
3848
3849   // first draw local network player ...
3850   for (i = 0; i < MAX_PLAYERS; i++)
3851   {
3852     if (stored_player[i].connected_network &&
3853         stored_player[i].connected_locally)
3854     {
3855       char *player_name = getNetworkPlayerName(i + 1);
3856       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3857       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3858
3859       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3860
3861       ypos += player_yoffset;
3862     }
3863   }
3864
3865   // ... then draw all other network players
3866   for (i = 0; i < MAX_PLAYERS; i++)
3867   {
3868     if (stored_player[i].connected_network &&
3869         !stored_player[i].connected_locally)
3870     {
3871       char *player_name = getNetworkPlayerName(i + 1);
3872       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3873       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3874
3875       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3876
3877       ypos += player_yoffset;
3878     }
3879   }
3880 }
3881
3882 void DrawNetworkPlayers(void)
3883 {
3884   DrawNetworkPlayersExt(FALSE);
3885 }
3886
3887 void ClearNetworkPlayers(void)
3888 {
3889   DrawNetworkPlayersExt(TRUE);
3890 }
3891
3892 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3893                                     int graphic, int lx, int ly,
3894                                     int mask_mode)
3895 {
3896   int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3897
3898   if (mask_mode == USE_MASKING)
3899     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3900   else
3901     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3902 }
3903
3904 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3905                                   int graphic, int sync_frame, int mask_mode)
3906 {
3907   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3908
3909   if (mask_mode == USE_MASKING)
3910     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3911   else
3912     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3913 }
3914
3915 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3916                                   int graphic, int sync_frame, int tilesize,
3917                                   int mask_mode)
3918 {
3919   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3920
3921   if (mask_mode == USE_MASKING)
3922     DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3923   else
3924     DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3925 }
3926
3927 static void DrawGraphicAnimation(int x, int y, int graphic)
3928 {
3929   int lx = LEVELX(x), ly = LEVELY(y);
3930   int mask_mode = NO_MASKING;
3931
3932   if (!IN_SCR_FIELD(x, y))
3933     return;
3934
3935   if (game.use_masked_elements)
3936   {
3937     if (Tile[lx][ly] != EL_EMPTY)
3938     {
3939       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3940
3941       mask_mode = USE_MASKING;
3942     }
3943   }
3944
3945   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3946                           graphic, lx, ly, mask_mode);
3947
3948   MarkTileDirty(x, y);
3949 }
3950
3951 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3952 {
3953   int lx = LEVELX(x), ly = LEVELY(y);
3954   int mask_mode = NO_MASKING;
3955
3956   if (!IN_SCR_FIELD(x, y))
3957     return;
3958
3959   if (game.use_masked_elements)
3960   {
3961     if (Tile[lx][ly] != EL_EMPTY)
3962     {
3963       DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3964
3965       mask_mode = USE_MASKING;
3966     }
3967   }
3968
3969   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3970                           graphic, lx, ly, mask_mode);
3971
3972   MarkTileDirty(x, y);
3973 }
3974
3975 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3976 {
3977   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3978 }
3979
3980 void DrawLevelElementAnimation(int x, int y, int element)
3981 {
3982   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3983
3984   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3985 }
3986
3987 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3988 {
3989   int sx = SCREENX(x), sy = SCREENY(y);
3990
3991   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3992     return;
3993
3994   if (Tile[x][y] == EL_EMPTY)
3995     graphic = el2img(GfxElementEmpty[x][y]);
3996
3997   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3998     return;
3999
4000   if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4001     return;
4002
4003   DrawGraphicAnimation(sx, sy, graphic);
4004
4005 #if 1
4006   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4007     DrawLevelFieldCrumbled(x, y);
4008 #else
4009   if (GFX_CRUMBLED(Tile[x][y]))
4010     DrawLevelFieldCrumbled(x, y);
4011 #endif
4012 }
4013
4014 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4015 {
4016   int sx = SCREENX(x), sy = SCREENY(y);
4017   int graphic;
4018
4019   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4020     return;
4021
4022   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4023
4024   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4025     return;
4026
4027   DrawGraphicAnimation(sx, sy, graphic);
4028
4029   if (GFX_CRUMBLED(element))
4030     DrawLevelFieldCrumbled(x, y);
4031 }
4032
4033 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4034 {
4035   if (player->use_murphy)
4036   {
4037     // this works only because currently only one player can be "murphy" ...
4038     static int last_horizontal_dir = MV_LEFT;
4039     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4040
4041     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4042       last_horizontal_dir = move_dir;
4043
4044     if (graphic == IMG_SP_MURPHY)       // undefined => use special graphic
4045     {
4046       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4047
4048       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4049     }
4050
4051     return graphic;
4052   }
4053   else
4054     return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4055 }
4056
4057 static boolean equalGraphics(int graphic1, int graphic2)
4058 {
4059   struct GraphicInfo *g1 = &graphic_info[graphic1];
4060   struct GraphicInfo *g2 = &graphic_info[graphic2];
4061
4062   return (g1->bitmap      == g2->bitmap &&
4063           g1->src_x       == g2->src_x &&
4064           g1->src_y       == g2->src_y &&
4065           g1->anim_frames == g2->anim_frames &&
4066           g1->anim_delay  == g2->anim_delay &&
4067           g1->anim_mode   == g2->anim_mode);
4068 }
4069
4070 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4071
4072 enum
4073 {
4074   DRAW_PLAYER_STAGE_INIT = 0,
4075   DRAW_PLAYER_STAGE_LAST_FIELD,
4076   DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4077 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4078   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4079   DRAW_PLAYER_STAGE_PLAYER,
4080 #else
4081   DRAW_PLAYER_STAGE_PLAYER,
4082   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4083 #endif
4084   DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4085   DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4086
4087   NUM_DRAW_PLAYER_STAGES
4088 };
4089
4090 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4091 {
4092   static int static_last_player_graphic[MAX_PLAYERS];
4093   static int static_last_player_frame[MAX_PLAYERS];
4094   static boolean static_player_is_opaque[MAX_PLAYERS];
4095   static boolean draw_player[MAX_PLAYERS];
4096   int pnr = player->index_nr;
4097
4098   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4099   {
4100     static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4101     static_last_player_frame[pnr] = player->Frame;
4102     static_player_is_opaque[pnr] = FALSE;
4103
4104     draw_player[pnr] = TRUE;
4105   }
4106
4107   if (!draw_player[pnr])
4108     return;
4109
4110 #if DEBUG
4111   if (!IN_LEV_FIELD(player->jx, player->jy))
4112   {
4113     Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4114     Debug("draw:DrawPlayerExt", "This should never happen!");
4115
4116     draw_player[pnr] = FALSE;
4117
4118     return;
4119   }
4120 #endif
4121
4122   int last_player_graphic  = static_last_player_graphic[pnr];
4123   int last_player_frame    = static_last_player_frame[pnr];
4124   boolean player_is_opaque = static_player_is_opaque[pnr];
4125
4126   int jx = player->jx;
4127   int jy = player->jy;
4128   int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4129   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4130   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
4131   int last_jx = (player->is_moving ? jx - dx : jx);
4132   int last_jy = (player->is_moving ? jy - dy : jy);
4133   int next_jx = jx + dx;
4134   int next_jy = jy + dy;
4135   boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4136   int sx = SCREENX(jx);
4137   int sy = SCREENY(jy);
4138   int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4139   int syy = (move_dir == MV_UP   || move_dir == MV_DOWN  ? player->GfxPos : 0);
4140   int element = Tile[jx][jy];
4141   int last_element = Tile[last_jx][last_jy];
4142   int action = (player->is_pushing    ? ACTION_PUSHING         :
4143                 player->is_digging    ? ACTION_DIGGING         :
4144                 player->is_collecting ? ACTION_COLLECTING      :
4145                 player->is_moving     ? ACTION_MOVING          :
4146                 player->is_snapping   ? ACTION_SNAPPING        :
4147                 player->is_dropping   ? ACTION_DROPPING        :
4148                 player->is_waiting    ? player->action_waiting :
4149                 ACTION_DEFAULT);
4150
4151   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4152   {
4153     // ------------------------------------------------------------------------
4154     // initialize drawing the player
4155     // ------------------------------------------------------------------------
4156
4157     draw_player[pnr] = FALSE;
4158
4159     // GfxElement[][] is set to the element the player is digging or collecting;
4160     // remove also for off-screen player if the player is not moving anymore
4161     if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4162       GfxElement[jx][jy] = EL_UNDEFINED;
4163
4164     if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4165       return;
4166
4167     if (element == EL_EXPLOSION)
4168       return;
4169
4170     InitPlayerGfxAnimation(player, action, move_dir);
4171
4172     draw_player[pnr] = TRUE;
4173   }
4174   else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4175   {
4176     // ------------------------------------------------------------------------
4177     // draw things in the field the player is leaving, if needed
4178     // ------------------------------------------------------------------------
4179
4180     if (!IN_SCR_FIELD(sx, sy))
4181       draw_player[pnr] = FALSE;
4182
4183     if (!player->is_moving)
4184       return;
4185
4186     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4187     {
4188       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4189
4190       if (last_element == EL_DYNAMITE_ACTIVE ||
4191           last_element == EL_EM_DYNAMITE_ACTIVE ||
4192           last_element == EL_SP_DISK_RED_ACTIVE)
4193         DrawDynamite(last_jx, last_jy);
4194       else
4195         DrawLevelFieldThruMask(last_jx, last_jy);
4196     }
4197     else if (last_element == EL_DYNAMITE_ACTIVE ||
4198              last_element == EL_EM_DYNAMITE_ACTIVE ||
4199              last_element == EL_SP_DISK_RED_ACTIVE)
4200       DrawDynamite(last_jx, last_jy);
4201     else
4202       DrawLevelField(last_jx, last_jy);
4203   }
4204   else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4205   {
4206     // ------------------------------------------------------------------------
4207     // draw things behind the player, if needed
4208     // ------------------------------------------------------------------------
4209
4210     if (Back[jx][jy])
4211     {
4212       DrawLevelElement(jx, jy, Back[jx][jy]);
4213
4214       return;
4215     }
4216
4217     if (IS_ACTIVE_BOMB(element))
4218     {
4219       DrawLevelElement(jx, jy, EL_EMPTY);
4220
4221       return;
4222     }
4223
4224     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4225     {
4226       int old_element = GfxElement[jx][jy];
4227       int old_graphic = el_act_dir2img(old_element, action, move_dir);
4228       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4229
4230       if (GFX_CRUMBLED(old_element))
4231         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4232       else
4233         DrawScreenGraphic(sx, sy, old_graphic, frame);
4234
4235       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4236         static_player_is_opaque[pnr] = TRUE;
4237     }
4238     else
4239     {
4240       GfxElement[jx][jy] = EL_UNDEFINED;
4241
4242       // make sure that pushed elements are drawn with correct frame rate
4243       int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4244
4245       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4246         GfxFrame[jx][jy] = player->StepFrame;
4247
4248       DrawLevelField(jx, jy);
4249     }
4250   }
4251   else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4252   {
4253     // ------------------------------------------------------------------------
4254     // draw things the player is pushing, if needed
4255     // ------------------------------------------------------------------------
4256
4257     if (!player->is_pushing || !player->is_moving)
4258       return;
4259
4260     if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4261       return;
4262
4263     int gfx_frame = GfxFrame[jx][jy];
4264
4265     if (!IS_MOVING(jx, jy))             // push movement already finished
4266     {
4267       element = Tile[next_jx][next_jy];
4268       gfx_frame = GfxFrame[next_jx][next_jy];
4269     }
4270
4271     int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4272     int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4273     int frame = getGraphicAnimationFrame(graphic, sync_frame);
4274
4275     // draw background element under pushed element (like the Sokoban field)
4276     if (game.use_masked_pushing && IS_MOVING(jx, jy))
4277     {
4278       // this allows transparent pushing animation over non-black background
4279
4280       if (Back[jx][jy])
4281         DrawLevelElement(jx, jy, Back[jx][jy]);
4282       else
4283         DrawLevelElement(jx, jy, EL_EMPTY);
4284     }
4285
4286     if (Back[next_jx][next_jy])
4287       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4288     else
4289       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4290
4291     int px = SCREENX(jx), py = SCREENY(jy);
4292     int pxx = (TILEX - ABS(sxx)) * dx;
4293     int pyy = (TILEY - ABS(syy)) * dy;
4294
4295 #if 1
4296     // do not draw (EM style) pushing animation when pushing is finished
4297     // (two-tile animations usually do not contain start and end frame)
4298     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4299       DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4300     else
4301       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4302 #else
4303     // masked drawing is needed for EMC style (double) movement graphics
4304     // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4305     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4306 #endif
4307   }
4308   else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4309   {
4310     // ------------------------------------------------------------------------
4311     // draw player himself
4312     // ------------------------------------------------------------------------
4313
4314     int graphic = getPlayerGraphic(player, move_dir);
4315
4316     // in the case of changed player action or direction, prevent the current
4317     // animation frame from being restarted for identical animations
4318     if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4319       player->Frame = last_player_frame;
4320
4321     int frame = getGraphicAnimationFrame(graphic, player->Frame);
4322
4323     if (player_is_opaque)
4324       DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4325     else
4326       DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4327
4328     if (SHIELD_ON(player))
4329     {
4330       graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4331                  IMG_SHIELD_NORMAL_ACTIVE);
4332       frame = getGraphicAnimationFrame(graphic, -1);
4333
4334       DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4335     }
4336   }
4337   else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4338   {
4339     // ------------------------------------------------------------------------
4340     // draw things in front of player (active dynamite or dynabombs)
4341     // ------------------------------------------------------------------------
4342
4343     if (IS_ACTIVE_BOMB(element))
4344     {
4345       int graphic = el2img(element);
4346       int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4347
4348       if (game.emulation == EMU_SUPAPLEX)
4349         DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4350       else
4351         DrawGraphicThruMask(sx, sy, graphic, frame);
4352     }
4353
4354     if (player_is_moving && last_element == EL_EXPLOSION)
4355     {
4356       int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4357                      GfxElement[last_jx][last_jy] :  EL_EMPTY);
4358       int graphic = el_act2img(element, ACTION_EXPLODING);
4359       int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4360       int phase = ExplodePhase[last_jx][last_jy] - 1;
4361       int frame = getGraphicAnimationFrame(graphic, phase - delay);
4362
4363       if (phase >= delay)
4364         DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4365     }
4366   }
4367   else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4368   {
4369     // ------------------------------------------------------------------------
4370     // draw elements the player is just walking/passing through/under
4371     // ------------------------------------------------------------------------
4372
4373     if (player_is_moving)
4374     {
4375       // handle the field the player is leaving ...
4376       if (IS_ACCESSIBLE_INSIDE(last_element))
4377         DrawLevelField(last_jx, last_jy);
4378       else if (IS_ACCESSIBLE_UNDER(last_element))
4379         DrawLevelFieldThruMask(last_jx, last_jy);
4380     }
4381
4382     // do not redraw accessible elements if the player is just pushing them
4383     if (!player_is_moving || !player->is_pushing)
4384     {
4385       // ... and the field the player is entering
4386       if (IS_ACCESSIBLE_INSIDE(element))
4387         DrawLevelField(jx, jy);
4388       else if (IS_ACCESSIBLE_UNDER(element))
4389         DrawLevelFieldThruMask(jx, jy);
4390     }
4391
4392     MarkTileDirty(sx, sy);
4393   }
4394 }
4395
4396 void DrawPlayer(struct PlayerInfo *player)
4397 {
4398   int i;
4399
4400   for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4401     DrawPlayerExt(player, i);
4402 }
4403
4404 void DrawAllPlayers(void)
4405 {
4406   int i, j;
4407
4408   for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4409     for (j = 0; j < MAX_PLAYERS; j++)
4410       if (stored_player[j].active)
4411         DrawPlayerExt(&stored_player[j], i);
4412 }
4413
4414 void DrawPlayerField(int x, int y)
4415 {
4416   if (!IS_PLAYER(x, y))
4417     return;
4418
4419   DrawPlayer(PLAYERINFO(x, y));
4420 }
4421
4422 // ----------------------------------------------------------------------------
4423
4424 void WaitForEventToContinue(void)
4425 {
4426   boolean first_wait = TRUE;
4427   boolean still_wait = TRUE;
4428
4429   if (program.headless)
4430     return;
4431
4432   // simulate releasing mouse button over last gadget, if still pressed
4433   if (button_status)
4434     HandleGadgets(-1, -1, 0);
4435
4436   button_status = MB_RELEASED;
4437
4438   ClearEventQueue();
4439   ClearPlayerAction();
4440
4441   while (still_wait)
4442   {
4443     Event event;
4444
4445     if (NextValidEvent(&event))
4446     {
4447       switch (event.type)
4448       {
4449         case EVENT_BUTTONPRESS:
4450         case EVENT_FINGERPRESS:
4451           first_wait = FALSE;
4452           break;
4453
4454         case EVENT_BUTTONRELEASE:
4455         case EVENT_FINGERRELEASE:
4456           still_wait = first_wait;
4457           break;
4458
4459         case EVENT_KEYPRESS:
4460         case SDL_CONTROLLERBUTTONDOWN:
4461         case SDL_JOYBUTTONDOWN:
4462           still_wait = FALSE;
4463           break;
4464
4465         default:
4466           HandleOtherEvents(&event);
4467           break;
4468       }
4469     }
4470     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4471     {
4472       still_wait = FALSE;
4473     }
4474
4475     if (!PendingEvent())
4476       BackToFront();
4477   }
4478 }
4479
4480 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4481 {
4482   boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4483   int draw_buffer_last = GetDrawtoField();
4484   int width  = request.width;
4485   int height = request.height;
4486   int sx, sy;
4487   int result;
4488
4489   setRequestPosition(&sx, &sy, FALSE);
4490
4491   button_status = MB_RELEASED;
4492
4493   request_gadget_id = -1;
4494   result = -1;
4495
4496   while (result < 0)
4497   {
4498     if (game_ended)
4499     {
4500       SetDrawtoField(draw_buffer_game);
4501
4502       HandleGameActions();
4503
4504       SetDrawtoField(DRAW_TO_BACKBUFFER);
4505     }
4506
4507     if (PendingEvent())
4508     {
4509       Event event;
4510
4511       while (NextValidEvent(&event))
4512       {
4513         switch (event.type)
4514         {
4515           case EVENT_BUTTONPRESS:
4516           case EVENT_BUTTONRELEASE:
4517           case EVENT_MOTIONNOTIFY:
4518           {
4519             DrawBuffer *drawto_last = drawto;
4520             int mx, my;
4521
4522             if (event.type == EVENT_MOTIONNOTIFY)
4523             {
4524               if (!button_status)
4525                 continue;
4526
4527               motion_status = TRUE;
4528               mx = ((MotionEvent *) &event)->x;
4529               my = ((MotionEvent *) &event)->y;
4530             }
4531             else
4532             {
4533               motion_status = FALSE;
4534               mx = ((ButtonEvent *) &event)->x;
4535               my = ((ButtonEvent *) &event)->y;
4536               if (event.type == EVENT_BUTTONPRESS)
4537                 button_status = ((ButtonEvent *) &event)->button;
4538               else
4539                 button_status = MB_RELEASED;
4540             }
4541
4542             if (global.use_envelope_request)
4543             {
4544               // draw changed button states to temporary bitmap
4545               drawto = bitmap_db_store_1;
4546             }
4547
4548             // this sets 'request_gadget_id'
4549             HandleGadgets(mx, my, button_status);
4550
4551             if (global.use_envelope_request)
4552             {
4553               // restore pointer to drawing buffer
4554               drawto = drawto_last;
4555
4556               // prepare complete envelope request from temporary bitmap
4557               PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4558                                              width, height);
4559             }
4560
4561             switch (request_gadget_id)
4562             {
4563               case TOOL_CTRL_ID_YES:
4564               case TOOL_CTRL_ID_TOUCH_YES:
4565                 result = TRUE;
4566                 break;
4567               case TOOL_CTRL_ID_NO:
4568               case TOOL_CTRL_ID_TOUCH_NO:
4569                 result = FALSE;
4570                 break;
4571               case TOOL_CTRL_ID_CONFIRM:
4572               case TOOL_CTRL_ID_TOUCH_CONFIRM:
4573                 result = TRUE | FALSE;
4574                 break;
4575
4576               case TOOL_CTRL_ID_PLAYER_1:
4577                 result = 1;
4578                 break;
4579               case TOOL_CTRL_ID_PLAYER_2:
4580                 result = 2;
4581                 break;
4582               case TOOL_CTRL_ID_PLAYER_3:
4583                 result = 3;
4584                 break;
4585               case TOOL_CTRL_ID_PLAYER_4:
4586                 result = 4;
4587                 break;
4588
4589               default:
4590                 // only check clickable animations if no request gadget clicked
4591                 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4592                 break;
4593             }
4594
4595             break;
4596           }
4597
4598           case SDL_WINDOWEVENT:
4599             HandleWindowEvent((WindowEvent *) &event);
4600             break;
4601
4602           case SDL_APP_WILLENTERBACKGROUND:
4603           case SDL_APP_DIDENTERBACKGROUND:
4604           case SDL_APP_WILLENTERFOREGROUND:
4605           case SDL_APP_DIDENTERFOREGROUND:
4606             HandlePauseResumeEvent((PauseResumeEvent *) &event);
4607             break;
4608
4609           case EVENT_KEYPRESS:
4610           {
4611             Key key = GetEventKey((KeyEvent *)&event);
4612
4613             switch (key)
4614             {
4615               case KSYM_space:
4616                 if (req_state & REQ_CONFIRM)
4617                   result = 1;
4618                 break;
4619
4620               case KSYM_Return:
4621               case KSYM_y:
4622               case KSYM_Y:
4623               case KSYM_Select:
4624               case KSYM_Menu:
4625 #if defined(KSYM_Rewind)
4626               case KSYM_Rewind:         // for Amazon Fire TV remote
4627 #endif
4628                 result = 1;
4629                 break;
4630
4631               case KSYM_Escape:
4632               case KSYM_n:
4633               case KSYM_N:
4634               case KSYM_Back:
4635 #if defined(KSYM_FastForward)
4636               case KSYM_FastForward:    // for Amazon Fire TV remote
4637 #endif
4638                 result = 0;
4639                 break;
4640
4641               default:
4642                 HandleKeysDebug(key, KEY_PRESSED);
4643                 break;
4644             }
4645
4646             if (req_state & REQ_PLAYER)
4647             {
4648               int old_player_nr = setup.network_player_nr;
4649
4650               if (result != -1)
4651                 result = old_player_nr + 1;
4652
4653               switch (key)
4654               {
4655                 case KSYM_space:
4656                   result = old_player_nr + 1;
4657                   break;
4658
4659                 case KSYM_Up:
4660                 case KSYM_1:
4661                   result = 1;
4662                   break;
4663
4664                 case KSYM_Right:
4665                 case KSYM_2:
4666                   result = 2;
4667                   break;
4668
4669                 case KSYM_Down:
4670                 case KSYM_3:
4671                   result = 3;
4672                   break;
4673
4674                 case KSYM_Left:
4675                 case KSYM_4:
4676                   result = 4;
4677                   break;
4678
4679                 default:
4680                   break;
4681               }
4682             }
4683
4684             break;
4685           }
4686
4687           case EVENT_FINGERRELEASE:
4688           case EVENT_KEYRELEASE:
4689             ClearPlayerAction();
4690             break;
4691
4692           case SDL_CONTROLLERBUTTONDOWN:
4693             switch (event.cbutton.button)
4694             {
4695               case SDL_CONTROLLER_BUTTON_A:
4696               case SDL_CONTROLLER_BUTTON_X:
4697               case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4698               case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4699                 result = 1;
4700                 break;
4701
4702               case SDL_CONTROLLER_BUTTON_B:
4703               case SDL_CONTROLLER_BUTTON_Y:
4704               case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4705               case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4706               case SDL_CONTROLLER_BUTTON_BACK:
4707                 result = 0;
4708                 break;
4709             }
4710
4711             if (req_state & REQ_PLAYER)
4712             {
4713               int old_player_nr = setup.network_player_nr;
4714
4715               if (result != -1)
4716                 result = old_player_nr + 1;
4717
4718               switch (event.cbutton.button)
4719               {
4720                 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4721                 case SDL_CONTROLLER_BUTTON_Y:
4722                   result = 1;
4723                   break;
4724
4725                 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4726                 case SDL_CONTROLLER_BUTTON_B:
4727                   result = 2;
4728                   break;
4729
4730                 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4731                 case SDL_CONTROLLER_BUTTON_A:
4732                   result = 3;
4733                   break;
4734
4735                 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4736                 case SDL_CONTROLLER_BUTTON_X:
4737                   result = 4;
4738                   break;
4739
4740                 default:
4741                   break;
4742               }
4743             }
4744
4745             break;
4746
4747           case SDL_CONTROLLERBUTTONUP:
4748             HandleJoystickEvent(&event);
4749             ClearPlayerAction();
4750             break;
4751
4752           default:
4753             HandleOtherEvents(&event);
4754             break;
4755         }
4756       }
4757     }
4758     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4759     {
4760       int joy = AnyJoystick();
4761
4762       if (joy & JOY_BUTTON_1)
4763         result = 1;
4764       else if (joy & JOY_BUTTON_2)
4765         result = 0;
4766     }
4767     else if (AnyJoystick())
4768     {
4769       int joy = AnyJoystick();
4770
4771       if (req_state & REQ_PLAYER)
4772       {
4773         if (joy & JOY_UP)
4774           result = 1;
4775         else if (joy & JOY_RIGHT)
4776           result = 2;
4777         else if (joy & JOY_DOWN)
4778           result = 3;
4779         else if (joy & JOY_LEFT)
4780           result = 4;
4781       }
4782     }
4783
4784     BackToFront();
4785   }
4786
4787   SetDrawtoField(draw_buffer_last);
4788
4789   return result;
4790 }
4791
4792 static void DoRequestBefore(void)
4793 {
4794   boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4795
4796   // when showing request dialog after game ended, deactivate game panel
4797   if (game_ended)
4798     game.panel.active = FALSE;
4799
4800   if (game_status == GAME_MODE_PLAYING)
4801     BlitScreenToBitmap(backbuffer);
4802
4803   // disable deactivated drawing when quick-loading level tape recording
4804   if (tape.playing && tape.deactivate_display)
4805     TapeDeactivateDisplayOff(TRUE);
4806
4807   SetMouseCursor(CURSOR_DEFAULT);
4808
4809   // pause network game while waiting for request to answer
4810   if (network.enabled &&
4811       game_status == GAME_MODE_PLAYING &&
4812       !game.all_players_gone)
4813     SendToServer_PausePlaying();
4814
4815   // simulate releasing mouse button over last gadget, if still pressed
4816   if (button_status)
4817     HandleGadgets(-1, -1, 0);
4818
4819   UnmapAllGadgets();
4820 }
4821
4822 static void DoRequestAfter(void)
4823 {
4824   RemapAllGadgets();
4825
4826   if (game_status == GAME_MODE_PLAYING)
4827   {
4828     SetPanelBackground();
4829     SetDrawBackgroundMask(REDRAW_DOOR_1);
4830   }
4831   else
4832   {
4833     SetDrawBackgroundMask(REDRAW_FIELD);
4834   }
4835
4836   // continue network game after request
4837   if (network.enabled &&
4838       game_status == GAME_MODE_PLAYING &&
4839       !game.all_players_gone)
4840     SendToServer_ContinuePlaying();
4841
4842   // restore deactivated drawing when quick-loading level tape recording
4843   if (tape.playing && tape.deactivate_display)
4844     TapeDeactivateDisplayOn();
4845 }
4846
4847 static void setRequestDoorTextProperties(char *text,
4848                                          int text_spacing,
4849                                          int line_spacing,
4850                                          int *set_font_nr,
4851                                          int *set_max_lines,
4852                                          int *set_max_line_length)
4853 {
4854   struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4855   struct TextPosInfo *pos = &request.button.confirm;
4856   int button_ypos = pos->y;
4857   int font_nr = FONT_TEXT_2;
4858   int font_width = getFontWidth(font_nr);
4859   int font_height = getFontHeight(font_nr);
4860   int line_height = font_height + line_spacing;
4861   int max_text_width  = vp_door_1->width;
4862   int max_text_height = button_ypos - 2 * text_spacing;
4863   int max_line_length = max_text_width  / font_width;
4864   int max_lines       = max_text_height / line_height;
4865
4866   if (maxWordLengthInRequestString(text) > max_line_length)
4867   {
4868     font_nr = FONT_TEXT_1;
4869     font_width = getFontWidth(font_nr);
4870     max_line_length = max_text_width  / font_width;
4871   }
4872
4873   *set_font_nr = font_nr;
4874   *set_max_lines = max_lines;
4875   *set_max_line_length = max_line_length;
4876 }
4877
4878 static void DrawRequestDoorText(char *text)
4879 {
4880   char *text_ptr = text;
4881   int text_spacing = 8;
4882   int line_spacing = 2;
4883   int max_request_lines;
4884   int max_request_line_len;
4885   int font_nr;
4886   int ty;
4887
4888   // force DOOR font inside door area
4889   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4890
4891   setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4892                                &max_request_lines, &max_request_line_len);
4893
4894   for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4895   {
4896     char text_line[max_request_line_len + 1];
4897     int tx, tl, tc = 0;
4898
4899     if (!*text_ptr)
4900       break;
4901
4902     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4903     {
4904       tc = *(text_ptr + tx);
4905       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4906         break;
4907     }
4908
4909     if ((tc == '?' || tc == '!') && tl == 0)
4910       tl = 1;
4911
4912     if (!tl)
4913     { 
4914       text_ptr++; 
4915       ty--; 
4916       continue; 
4917     }
4918
4919     strncpy(text_line, text_ptr, tl);
4920     text_line[tl] = 0;
4921
4922     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4923              DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4924              text_line, font_nr);
4925
4926     text_ptr += tl + (tc == ' ' ? 1 : 0);
4927   }
4928
4929   ResetFontStatus();
4930 }
4931
4932 static int RequestDoor(char *text, unsigned int req_state)
4933 {
4934   unsigned int old_door_state = GetDoorState();
4935   int draw_buffer_last = GetDrawtoField();
4936   int result;
4937
4938   if (old_door_state & DOOR_OPEN_1)
4939   {
4940     CloseDoor(DOOR_CLOSE_1);
4941
4942     // save old door content
4943     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4944                0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
4945   }
4946
4947   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4948   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4949
4950   // clear door drawing field
4951   DrawBackground(DX, DY, DXSIZE, DYSIZE);
4952
4953   // write text for request
4954   DrawRequestDoorText(text);
4955
4956   MapToolButtons(req_state);
4957
4958   // copy request gadgets to door backbuffer
4959   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4960
4961   OpenDoor(DOOR_OPEN_1);
4962
4963   // ---------- handle request buttons ----------
4964   result = RequestHandleEvents(req_state, draw_buffer_last);
4965
4966   UnmapToolButtons();
4967
4968   if (!(req_state & REQ_STAY_OPEN))
4969   {
4970     CloseDoor(DOOR_CLOSE_1);
4971
4972     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4973         (req_state & REQ_REOPEN))
4974       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4975   }
4976
4977   return result;
4978 }
4979
4980 static int RequestEnvelope(char *text, unsigned int req_state)
4981 {
4982   int draw_buffer_last = GetDrawtoField();
4983   int result;
4984
4985   DrawEnvelopeRequest(text, req_state);
4986   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4987
4988   // ---------- handle request buttons ----------
4989   result = RequestHandleEvents(req_state, draw_buffer_last);
4990
4991   UnmapToolButtons();
4992
4993   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4994
4995   return result;
4996 }
4997
4998 int Request(char *text, unsigned int req_state)
4999 {
5000   boolean overlay_enabled = GetOverlayEnabled();
5001   int result;
5002
5003   game.request_active = TRUE;
5004
5005   SetOverlayEnabled(FALSE);
5006
5007   DoRequestBefore();
5008
5009   if (global.use_envelope_request)
5010     result = RequestEnvelope(text, req_state);
5011   else
5012     result = RequestDoor(text, req_state);
5013
5014   DoRequestAfter();
5015
5016   SetOverlayEnabled(overlay_enabled);
5017
5018   game.request_active = FALSE;
5019
5020   return result;
5021 }
5022
5023 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5024 {
5025   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5026   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5027   int compare_result;
5028
5029   if (dpo1->sort_priority != dpo2->sort_priority)
5030     compare_result = dpo1->sort_priority - dpo2->sort_priority;
5031   else
5032     compare_result = dpo1->nr - dpo2->nr;
5033
5034   return compare_result;
5035 }
5036
5037 void InitGraphicCompatibilityInfo_Doors(void)
5038 {
5039   struct
5040   {
5041     int door_token;
5042     int part_1, part_8;
5043     struct DoorInfo *door;
5044   }
5045   doors[] =
5046   {
5047     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
5048     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
5049
5050     { -1,       -1,                     -1,                     NULL    }
5051   };
5052   struct Rect door_rect_list[] =
5053   {
5054     { DX, DY, DXSIZE, DYSIZE },
5055     { VX, VY, VXSIZE, VYSIZE }
5056   };
5057   int i, j;
5058
5059   for (i = 0; doors[i].door_token != -1; i++)
5060   {
5061     int door_token = doors[i].door_token;
5062     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5063     int part_1 = doors[i].part_1;
5064     int part_8 = doors[i].part_8;
5065     int part_2 = part_1 + 1;
5066     int part_3 = part_1 + 2;
5067     struct DoorInfo *door = doors[i].door;
5068     struct Rect *door_rect = &door_rect_list[door_index];
5069     boolean door_gfx_redefined = FALSE;
5070
5071     // check if any door part graphic definitions have been redefined
5072
5073     for (j = 0; door_part_controls[j].door_token != -1; j++)
5074     {
5075       struct DoorPartControlInfo *dpc = &door_part_controls[j];
5076       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5077
5078       if (dpc->door_token == door_token && fi->redefined)
5079         door_gfx_redefined = TRUE;
5080     }
5081
5082     // check for old-style door graphic/animation modifications
5083
5084     if (!door_gfx_redefined)
5085     {
5086       if (door->anim_mode & ANIM_STATIC_PANEL)
5087       {
5088         door->panel.step_xoffset = 0;
5089         door->panel.step_yoffset = 0;
5090       }
5091
5092       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5093       {
5094         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5095         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5096         int num_door_steps, num_panel_steps;
5097
5098         // remove door part graphics other than the two default wings
5099
5100         for (j = 0; door_part_controls[j].door_token != -1; j++)
5101         {
5102           struct DoorPartControlInfo *dpc = &door_part_controls[j];
5103           struct GraphicInfo *g = &graphic_info[dpc->graphic];
5104
5105           if (dpc->graphic >= part_3 &&
5106               dpc->graphic <= part_8)
5107             g->bitmap = NULL;
5108         }
5109
5110         // set graphics and screen positions of the default wings
5111
5112         g_part_1->width  = door_rect->width;
5113         g_part_1->height = door_rect->height;
5114         g_part_2->width  = door_rect->width;
5115         g_part_2->height = door_rect->height;
5116         g_part_2->src_x = door_rect->width;
5117         g_part_2->src_y = g_part_1->src_y;
5118
5119         door->part_2.x = door->part_1.x;
5120         door->part_2.y = door->part_1.y;
5121
5122         if (door->width != -1)
5123         {
5124           g_part_1->width = door->width;
5125           g_part_2->width = door->width;
5126
5127           // special treatment for graphics and screen position of right wing
5128           g_part_2->src_x += door_rect->width - door->width;
5129           door->part_2.x  += door_rect->width - door->width;
5130         }
5131
5132         if (door->height != -1)
5133         {
5134           g_part_1->height = door->height;
5135           g_part_2->height = door->height;
5136
5137           // special treatment for graphics and screen position of bottom wing
5138           g_part_2->src_y += door_rect->height - door->height;
5139           door->part_2.y  += door_rect->height - door->height;
5140         }
5141
5142         // set animation delays for the default wings and panels
5143
5144         door->part_1.step_delay = door->step_delay;
5145         door->part_2.step_delay = door->step_delay;
5146         door->panel.step_delay  = door->step_delay;
5147
5148         // set animation draw order for the default wings
5149
5150         door->part_1.sort_priority = 2; // draw left wing over ...
5151         door->part_2.sort_priority = 1; //          ... right wing
5152
5153         // set animation draw offset for the default wings
5154
5155         if (door->anim_mode & ANIM_HORIZONTAL)
5156         {
5157           door->part_1.step_xoffset = door->step_offset;
5158           door->part_1.step_yoffset = 0;
5159           door->part_2.step_xoffset = door->step_offset * -1;
5160           door->part_2.step_yoffset = 0;
5161
5162           num_door_steps = g_part_1->width / door->step_offset;
5163         }
5164         else    // ANIM_VERTICAL
5165         {
5166           door->part_1.step_xoffset = 0;
5167           door->part_1.step_yoffset = door->step_offset;
5168           door->part_2.step_xoffset = 0;
5169           door->part_2.step_yoffset = door->step_offset * -1;
5170
5171           num_door_steps = g_part_1->height / door->step_offset;
5172         }
5173
5174         // set animation draw offset for the default panels
5175
5176         if (door->step_offset > 1)
5177         {
5178           num_panel_steps = 2 * door_rect->height / door->step_offset;
5179           door->panel.start_step = num_panel_steps - num_door_steps;
5180           door->panel.start_step_closing = door->panel.start_step;
5181         }
5182         else
5183         {
5184           num_panel_steps = door_rect->height / door->step_offset;
5185           door->panel.start_step = num_panel_steps - num_door_steps / 2;
5186           door->panel.start_step_closing = door->panel.start_step;
5187           door->panel.step_delay *= 2;
5188         }
5189       }
5190     }
5191   }
5192 }
5193
5194 void InitDoors(void)
5195 {
5196   int i;
5197
5198   for (i = 0; door_part_controls[i].door_token != -1; i++)
5199   {
5200     struct DoorPartControlInfo *dpc = &door_part_controls[i];
5201     struct DoorPartOrderInfo *dpo = &door_part_order[i];
5202
5203     // initialize "start_step_opening" and "start_step_closing", if needed
5204     if (dpc->pos->start_step_opening == 0 &&
5205         dpc->pos->start_step_closing == 0)
5206     {
5207       // dpc->pos->start_step_opening = dpc->pos->start_step;
5208       dpc->pos->start_step_closing = dpc->pos->start_step;
5209     }
5210
5211     // fill structure for door part draw order (sorted below)
5212     dpo->nr = i;
5213     dpo->sort_priority = dpc->pos->sort_priority;
5214   }
5215
5216   // sort door part controls according to sort_priority and graphic number
5217   qsort(door_part_order, MAX_DOOR_PARTS,
5218         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5219 }
5220
5221 unsigned int OpenDoor(unsigned int door_state)
5222 {
5223   if (door_state & DOOR_COPY_BACK)
5224   {
5225     if (door_state & DOOR_OPEN_1)
5226       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5227                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5228
5229     if (door_state & DOOR_OPEN_2)
5230       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5231                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5232
5233     door_state &= ~DOOR_COPY_BACK;
5234   }
5235
5236   return MoveDoor(door_state);
5237 }
5238
5239 unsigned int CloseDoor(unsigned int door_state)
5240 {
5241   unsigned int old_door_state = GetDoorState();
5242
5243   if (!(door_state & DOOR_NO_COPY_BACK))
5244   {
5245     if (old_door_state & DOOR_OPEN_1)
5246       BlitBitmap(backbuffer, bitmap_db_door_1,
5247                  DX, DY, DXSIZE, DYSIZE, 0, 0);
5248
5249     if (old_door_state & DOOR_OPEN_2)
5250       BlitBitmap(backbuffer, bitmap_db_door_2,
5251                  VX, VY, VXSIZE, VYSIZE, 0, 0);
5252
5253     door_state &= ~DOOR_NO_COPY_BACK;
5254   }
5255
5256   return MoveDoor(door_state);
5257 }
5258
5259 unsigned int GetDoorState(void)
5260 {
5261   return MoveDoor(DOOR_GET_STATE);
5262 }
5263
5264 unsigned int SetDoorState(unsigned int door_state)
5265 {
5266   return MoveDoor(door_state | DOOR_SET_STATE);
5267 }
5268
5269 static int euclid(int a, int b)
5270 {
5271   return (b ? euclid(b, a % b) : a);
5272 }
5273
5274 unsigned int MoveDoor(unsigned int door_state)
5275 {
5276   struct Rect door_rect_list[] =
5277   {
5278     { DX, DY, DXSIZE, DYSIZE },
5279     { VX, VY, VXSIZE, VYSIZE }
5280   };
5281   static int door1 = DOOR_CLOSE_1;
5282   static int door2 = DOOR_CLOSE_2;
5283   DelayCounter door_delay = { 0 };
5284   int i;
5285
5286   if (door_state == DOOR_GET_STATE)
5287     return (door1 | door2);
5288
5289   if (door_state & DOOR_SET_STATE)
5290   {
5291     if (door_state & DOOR_ACTION_1)
5292       door1 = door_state & DOOR_ACTION_1;
5293     if (door_state & DOOR_ACTION_2)
5294       door2 = door_state & DOOR_ACTION_2;
5295
5296     return (door1 | door2);
5297   }
5298
5299   if (!(door_state & DOOR_FORCE_REDRAW))
5300   {
5301     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5302       door_state &= ~DOOR_OPEN_1;
5303     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5304       door_state &= ~DOOR_CLOSE_1;
5305     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5306       door_state &= ~DOOR_OPEN_2;
5307     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5308       door_state &= ~DOOR_CLOSE_2;
5309   }
5310
5311   if (global.autoplay_leveldir)
5312   {
5313     door_state |= DOOR_NO_DELAY;
5314     door_state &= ~DOOR_CLOSE_ALL;
5315   }
5316
5317   if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5318     door_state |= DOOR_NO_DELAY;
5319
5320   if (door_state & DOOR_ACTION)
5321   {
5322     boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5323     boolean door_panel_drawn[NUM_DOORS];
5324     boolean panel_has_doors[NUM_DOORS];
5325     boolean door_part_skip[MAX_DOOR_PARTS];
5326     boolean door_part_done[MAX_DOOR_PARTS];
5327     boolean door_part_done_all;
5328     int num_steps[MAX_DOOR_PARTS];
5329     int max_move_delay = 0;     // delay for complete animations of all doors
5330     int max_step_delay = 0;     // delay (ms) between two animation frames
5331     int num_move_steps = 0;     // number of animation steps for all doors
5332     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
5333     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
5334     int start = 0;
5335     int k;
5336
5337     for (i = 0; i < NUM_DOORS; i++)
5338       panel_has_doors[i] = FALSE;
5339
5340     for (i = 0; i < MAX_DOOR_PARTS; i++)
5341     {
5342       struct DoorPartControlInfo *dpc = &door_part_controls[i];
5343       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5344       int door_token = dpc->door_token;
5345
5346       door_part_done[i] = FALSE;
5347       door_part_skip[i] = (!(door_state & door_token) ||
5348                            !g->bitmap);
5349     }
5350
5351     for (i = 0; i < MAX_DOOR_PARTS; i++)
5352     {
5353       int nr = door_part_order[i].nr;
5354       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5355       struct DoorPartPosInfo *pos = dpc->pos;
5356       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5357       int door_token = dpc->door_token;
5358       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5359       boolean is_panel = DOOR_PART_IS_PANEL(nr);
5360       int step_xoffset = ABS(pos->step_xoffset);
5361       int step_yoffset = ABS(pos->step_yoffset);
5362       int step_delay = pos->step_delay;
5363       int current_door_state = door_state & door_token;
5364       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5365       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5366       boolean part_opening = (is_panel ? door_closing : door_opening);
5367       int start_step = (part_opening ? pos->start_step_opening :
5368                         pos->start_step_closing);
5369       float move_xsize = (step_xoffset ? g->width  : 0);
5370       float move_ysize = (step_yoffset ? g->height : 0);
5371       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5372       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5373       int move_steps = (move_xsteps && move_ysteps ?
5374                         MIN(move_xsteps, move_ysteps) :
5375                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
5376       int move_delay = move_steps * step_delay;
5377
5378       if (door_part_skip[nr])
5379         continue;
5380
5381       max_move_delay = MAX(max_move_delay, move_delay);
5382       max_step_delay = (max_step_delay == 0 ? step_delay :
5383                         euclid(max_step_delay, step_delay));
5384       num_steps[nr] = move_steps;
5385
5386       if (!is_panel)
5387       {
5388         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5389
5390         panel_has_doors[door_index] = TRUE;
5391       }
5392     }
5393
5394     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
5395
5396     num_move_steps = max_move_delay / max_step_delay;
5397     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5398
5399     door_delay.value = max_step_delay;
5400
5401     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5402     {
5403       start = num_move_steps - 1;
5404     }
5405     else
5406     {
5407       // opening door sound has priority over simultaneously closing door
5408       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5409       {
5410         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5411
5412         if (door_state & DOOR_OPEN_1)
5413           PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5414         if (door_state & DOOR_OPEN_2)
5415           PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5416       }
5417       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5418       {
5419         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5420
5421         if (door_state & DOOR_CLOSE_1)
5422           PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5423         if (door_state & DOOR_CLOSE_2)
5424           PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5425       }
5426     }
5427
5428     for (k = start; k < num_move_steps; k++)
5429     {
5430       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
5431
5432       door_part_done_all = TRUE;
5433
5434       for (i = 0; i < NUM_DOORS; i++)
5435         door_panel_drawn[i] = FALSE;
5436
5437       for (i = 0; i < MAX_DOOR_PARTS; i++)
5438       {
5439         int nr = door_part_order[i].nr;
5440         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5441         struct DoorPartPosInfo *pos = dpc->pos;
5442         struct GraphicInfo *g = &graphic_info[dpc->graphic];
5443         int door_token = dpc->door_token;
5444         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5445         boolean is_panel = DOOR_PART_IS_PANEL(nr);
5446         boolean is_panel_and_door_has_closed = FALSE;
5447         struct Rect *door_rect = &door_rect_list[door_index];
5448         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5449                                   bitmap_db_door_2);
5450         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5451         int current_door_state = door_state & door_token;
5452         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5453         boolean door_closing = !door_opening;
5454         boolean part_opening = (is_panel ? door_closing : door_opening);
5455         boolean part_closing = !part_opening;
5456         int start_step = (part_opening ? pos->start_step_opening :
5457                           pos->start_step_closing);
5458         int step_delay = pos->step_delay;
5459         int step_factor = step_delay / max_step_delay;
5460         int k1 = (step_factor ? k / step_factor + 1 : k);
5461         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5462         int kk = MAX(0, k2);
5463         int g_src_x = 0;
5464         int g_src_y = 0;
5465         int src_x, src_y, src_xx, src_yy;
5466         int dst_x, dst_y, dst_xx, dst_yy;
5467         int width, height;
5468
5469         if (door_part_skip[nr])
5470           continue;
5471
5472         if (!(door_state & door_token))
5473           continue;
5474
5475         if (!g->bitmap)
5476           continue;
5477
5478         if (!is_panel)
5479         {
5480           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5481           int kk_door = MAX(0, k2_door);
5482           int sync_frame = kk_door * door_delay.value;
5483           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5484
5485           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5486                                 &g_src_x, &g_src_y);
5487         }
5488
5489         // draw door panel
5490
5491         if (!door_panel_drawn[door_index])
5492         {
5493           ClearRectangle(drawto, door_rect->x, door_rect->y,
5494                          door_rect->width, door_rect->height);
5495
5496           door_panel_drawn[door_index] = TRUE;
5497         }
5498
5499         // draw opening or closing door parts
5500
5501         if (pos->step_xoffset < 0)      // door part on right side
5502         {
5503           src_xx = 0;
5504           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5505           width = g->width;
5506
5507           if (dst_xx + width > door_rect->width)
5508             width = door_rect->width - dst_xx;
5509         }
5510         else                            // door part on left side
5511         {
5512           src_xx = 0;
5513           dst_xx = pos->x - kk * pos->step_xoffset;
5514
5515           if (dst_xx < 0)
5516           {
5517             src_xx = ABS(dst_xx);
5518             dst_xx = 0;
5519           }
5520
5521           width = g->width - src_xx;
5522
5523           if (width > door_rect->width)
5524             width = door_rect->width;
5525
5526           // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5527         }
5528
5529         if (pos->step_yoffset < 0)      // door part on bottom side
5530         {
5531           src_yy = 0;
5532           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5533           height = g->height;
5534
5535           if (dst_yy + height > door_rect->height)
5536             height = door_rect->height - dst_yy;
5537         }
5538         else                            // door part on top side
5539         {
5540           src_yy = 0;
5541           dst_yy = pos->y - kk * pos->step_yoffset;
5542
5543           if (dst_yy < 0)
5544           {
5545             src_yy = ABS(dst_yy);
5546             dst_yy = 0;
5547           }
5548
5549           height = g->height - src_yy;
5550         }
5551
5552         src_x = g_src_x + src_xx;
5553         src_y = g_src_y + src_yy;
5554
5555         dst_x = door_rect->x + dst_xx;
5556         dst_y = door_rect->y + dst_yy;
5557
5558         is_panel_and_door_has_closed =
5559           (is_panel &&
5560            door_closing &&
5561            panel_has_doors[door_index] &&
5562            k >= num_move_steps_doors_only - 1);
5563
5564         if (width  >= 0 && width  <= g->width &&
5565             height >= 0 && height <= g->height &&
5566             !is_panel_and_door_has_closed)
5567         {
5568           if (is_panel || !pos->draw_masked)
5569             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5570                        dst_x, dst_y);
5571           else
5572             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5573                              dst_x, dst_y);
5574         }
5575
5576         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5577
5578         if ((part_opening && (width < 0         || height < 0)) ||
5579             (part_closing && (width >= g->width && height >= g->height)))
5580           door_part_done[nr] = TRUE;
5581
5582         // continue door part animations, but not panel after door has closed
5583         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5584           door_part_done_all = FALSE;
5585       }
5586
5587       if (!(door_state & DOOR_NO_DELAY))
5588       {
5589         if (game_ended)
5590           HandleGameActions();
5591
5592         BackToFront();
5593
5594         SkipUntilDelayReached(&door_delay, &k, last_frame);
5595
5596         // prevent OS (Windows) from complaining about program not responding
5597         CheckQuitEvent();
5598       }
5599
5600       if (door_part_done_all)
5601         break;
5602     }
5603
5604     if (!(door_state & DOOR_NO_DELAY))
5605     {
5606       // wait for specified door action post delay
5607       if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5608         door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5609       else if (door_state & DOOR_ACTION_1)
5610         door_delay.value = door_1.post_delay;
5611       else if (door_state & DOOR_ACTION_2)
5612         door_delay.value = door_2.post_delay;
5613
5614       while (!DelayReached(&door_delay))
5615       {
5616         if (game_ended)
5617           HandleGameActions();
5618
5619         BackToFront();
5620       }
5621     }
5622   }
5623
5624   if (door_state & DOOR_ACTION_1)
5625     door1 = door_state & DOOR_ACTION_1;
5626   if (door_state & DOOR_ACTION_2)
5627     door2 = door_state & DOOR_ACTION_2;
5628
5629   // draw masked border over door area
5630   DrawMaskedBorder(REDRAW_DOOR_1);
5631   DrawMaskedBorder(REDRAW_DOOR_2);
5632
5633   ClearAutoRepeatKeyEvents();
5634
5635   return (door1 | door2);
5636 }
5637
5638 static boolean useSpecialEditorDoor(void)
5639 {
5640   int graphic = IMG_GLOBAL_BORDER_EDITOR;
5641   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5642
5643   // do not draw special editor door if editor border defined or redefined
5644   if (graphic_info[graphic].bitmap != NULL || redefined)
5645     return FALSE;
5646
5647   // do not draw special editor door if global border defined to be empty
5648   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5649     return FALSE;
5650
5651   // do not draw special editor door if viewport definitions do not match
5652   if (EX != VX ||
5653       EY >= VY ||
5654       EXSIZE != VXSIZE ||
5655       EY + EYSIZE != VY + VYSIZE)
5656     return FALSE;
5657
5658   return TRUE;
5659 }
5660
5661 void DrawSpecialEditorDoor(void)
5662 {
5663   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5664   int top_border_width = gfx1->width;
5665   int top_border_height = gfx1->height;
5666   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5667   int ex = EX - outer_border;
5668   int ey = EY - outer_border;
5669   int vy = VY - outer_border;
5670   int exsize = EXSIZE + 2 * outer_border;
5671
5672   if (!useSpecialEditorDoor())
5673     return;
5674
5675   // draw bigger level editor toolbox window
5676   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5677              top_border_width, top_border_height, ex, ey - top_border_height);
5678   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5679              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5680
5681   redraw_mask |= REDRAW_ALL;
5682 }
5683
5684 void UndrawSpecialEditorDoor(void)
5685 {
5686   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5687   int top_border_width = gfx1->width;
5688   int top_border_height = gfx1->height;
5689   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5690   int ex = EX - outer_border;
5691   int ey = EY - outer_border;
5692   int ey_top = ey - top_border_height;
5693   int exsize = EXSIZE + 2 * outer_border;
5694   int eysize = EYSIZE + 2 * outer_border;
5695
5696   if (!useSpecialEditorDoor())
5697     return;
5698
5699   // draw normal tape recorder window
5700   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5701   {
5702     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5703                ex, ey_top, top_border_width, top_border_height,
5704                ex, ey_top);
5705     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5706                ex, ey, exsize, eysize, ex, ey);
5707   }
5708   else
5709   {
5710     // if screen background is set to "[NONE]", clear editor toolbox window
5711     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5712     ClearRectangle(drawto, ex, ey, exsize, eysize);
5713   }
5714
5715   redraw_mask |= REDRAW_ALL;
5716 }
5717
5718
5719 // ---------- new tool button stuff -------------------------------------------
5720
5721 static struct
5722 {
5723   int graphic;
5724   struct TextPosInfo *pos;
5725   int gadget_id;
5726   boolean is_touch_button;
5727   char *infotext;
5728 } toolbutton_info[NUM_TOOL_BUTTONS] =
5729 {
5730   {
5731     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
5732     TOOL_CTRL_ID_YES, FALSE,            "yes"
5733   },
5734   {
5735     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5736     TOOL_CTRL_ID_NO, FALSE,             "no"
5737   },
5738   {
5739     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5740     TOOL_CTRL_ID_CONFIRM, FALSE,        "confirm"
5741   },
5742   {
5743     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5744     TOOL_CTRL_ID_PLAYER_1, FALSE,       "player 1"
5745   },
5746   {
5747     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5748     TOOL_CTRL_ID_PLAYER_2, FALSE,       "player 2"
5749   },
5750   {
5751     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5752     TOOL_CTRL_ID_PLAYER_3, FALSE,       "player 3"
5753   },
5754   {
5755     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5756     TOOL_CTRL_ID_PLAYER_4, FALSE,       "player 4"
5757   },
5758   {
5759     IMG_GFX_REQUEST_BUTTON_TOUCH_YES,   &request.button.touch_yes,
5760     TOOL_CTRL_ID_TOUCH_YES, TRUE,       "yes"
5761   },
5762   {
5763     IMG_GFX_REQUEST_BUTTON_TOUCH_NO,    &request.button.touch_no,
5764     TOOL_CTRL_ID_TOUCH_NO, TRUE,        "no"
5765   },
5766   {
5767     IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5768     TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE,   "confirm"
5769   }
5770 };
5771
5772 void CreateToolButtons(void)
5773 {
5774   int i;
5775
5776   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5777   {
5778     int graphic = toolbutton_info[i].graphic;
5779     struct GraphicInfo *gfx = &graphic_info[graphic];
5780     struct TextPosInfo *pos = toolbutton_info[i].pos;
5781     struct GadgetInfo *gi;
5782     Bitmap *deco_bitmap = None;
5783     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5784     unsigned int event_mask = GD_EVENT_RELEASED;
5785     boolean is_touch_button = toolbutton_info[i].is_touch_button;
5786     int base_x = (is_touch_button ? 0 : DX);
5787     int base_y = (is_touch_button ? 0 : DY);
5788     int gd_x = gfx->src_x;
5789     int gd_y = gfx->src_y;
5790     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5791     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5792     int x = pos->x;
5793     int y = pos->y;
5794     int id = i;
5795
5796     // do not use touch buttons if overlay touch buttons are disabled
5797     if (is_touch_button && !setup.touch.overlay_buttons)
5798       continue;
5799
5800     if (global.use_envelope_request && !is_touch_button)
5801     {
5802       setRequestPosition(&base_x, &base_y, TRUE);
5803
5804       // check if request buttons are outside of envelope and fix, if needed
5805       if (x < 0 || x + gfx->width  > request.width ||
5806           y < 0 || y + gfx->height > request.height)
5807       {
5808         if (id == TOOL_CTRL_ID_YES)
5809         {
5810           x = 0;
5811           y = request.height - 2 * request.border_size - gfx->height;
5812         }
5813         else if (id == TOOL_CTRL_ID_NO)
5814         {
5815           x = request.width  - 2 * request.border_size - gfx->width;
5816           y = request.height - 2 * request.border_size - gfx->height;
5817         }
5818         else if (id == TOOL_CTRL_ID_CONFIRM)
5819         {
5820           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5821           y = request.height - 2 * request.border_size - gfx->height;
5822         }
5823         else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5824         {
5825           int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5826
5827           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5828           y = request.height - 2 * request.border_size - gfx->height * 2;
5829
5830           x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5831           y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5832         }
5833       }
5834     }
5835
5836     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5837         pos->draw_player)
5838     {
5839       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5840
5841       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5842                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5843       deco_xpos = (gfx->width  - pos->size) / 2;
5844       deco_ypos = (gfx->height - pos->size) / 2;
5845     }
5846
5847     gi = CreateGadget(GDI_CUSTOM_ID, id,
5848                       GDI_IMAGE_ID, graphic,
5849                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5850                       GDI_X, base_x + x,
5851                       GDI_Y, base_y + y,
5852                       GDI_WIDTH, gfx->width,
5853                       GDI_HEIGHT, gfx->height,
5854                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5855                       GDI_STATE, GD_BUTTON_UNPRESSED,
5856                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5857                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5858                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5859                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5860                       GDI_DECORATION_SIZE, pos->size, pos->size,
5861                       GDI_DECORATION_SHIFTING, 1, 1,
5862                       GDI_DIRECT_DRAW, FALSE,
5863                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5864                       GDI_EVENT_MASK, event_mask,
5865                       GDI_CALLBACK_ACTION, HandleToolButtons,
5866                       GDI_END);
5867
5868     if (gi == NULL)
5869       Fail("cannot create gadget");
5870
5871     tool_gadget[id] = gi;
5872   }
5873 }
5874
5875 void FreeToolButtons(void)
5876 {
5877   int i;
5878
5879   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5880     FreeGadget(tool_gadget[i]);
5881 }
5882
5883 static void MapToolButtons(unsigned int req_state)
5884 {
5885   if (req_state & REQ_ASK)
5886   {
5887     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5888     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5889     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5890     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5891   }
5892   else if (req_state & REQ_CONFIRM)
5893   {
5894     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5895     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5896   }
5897   else if (req_state & REQ_PLAYER)
5898   {
5899     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5900     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5901     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5902     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5903   }
5904 }
5905
5906 static void UnmapToolButtons(void)
5907 {
5908   int i;
5909
5910   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5911     UnmapGadget(tool_gadget[i]);
5912 }
5913
5914 static void HandleToolButtons(struct GadgetInfo *gi)
5915 {
5916   request_gadget_id = gi->custom_id;
5917 }
5918
5919 static struct Mapping_EM_to_RND_object
5920 {
5921   int element_em;
5922   boolean is_rnd_to_em_mapping;         // unique mapping EM <-> RND
5923   boolean is_backside;                  // backside of moving element
5924
5925   int element_rnd;
5926   int action;
5927   int direction;
5928 }
5929 em_object_mapping_list[GAME_TILE_MAX + 1] =
5930 {
5931   {
5932     Zborder,                            FALSE,  FALSE,
5933     EL_EMPTY,                           -1, -1
5934   },
5935   {
5936     Zplayer,                            FALSE,  FALSE,
5937     EL_EMPTY,                           -1, -1
5938   },
5939
5940   {
5941     Zbug,                               FALSE,  FALSE,
5942     EL_EMPTY,                           -1, -1
5943   },
5944   {
5945     Ztank,                              FALSE,  FALSE,
5946     EL_EMPTY,                           -1, -1
5947   },
5948   {
5949     Zeater,                             FALSE,  FALSE,
5950     EL_EMPTY,                           -1, -1
5951   },
5952   {
5953     Zdynamite,                          FALSE,  FALSE,
5954     EL_EMPTY,                           -1, -1
5955   },
5956   {
5957     Zboom,                              FALSE,  FALSE,
5958     EL_EMPTY,                           -1, -1
5959   },
5960
5961   {
5962     Xchain,                             FALSE,  FALSE,
5963     EL_DEFAULT,                         ACTION_EXPLODING, -1
5964   },
5965   {
5966     Xboom_bug,                          FALSE,  FALSE,
5967     EL_BUG,                             ACTION_EXPLODING, -1
5968   },
5969   {
5970     Xboom_tank,                         FALSE,  FALSE,
5971     EL_SPACESHIP,                       ACTION_EXPLODING, -1
5972   },
5973   {
5974     Xboom_android,                      FALSE,  FALSE,
5975     EL_EMC_ANDROID,                     ACTION_OTHER, -1
5976   },
5977   {
5978     Xboom_1,                            FALSE,  FALSE,
5979     EL_DEFAULT,                         ACTION_EXPLODING, -1
5980   },
5981   {
5982     Xboom_2,                            FALSE,  FALSE,
5983     EL_DEFAULT,                         ACTION_EXPLODING, -1
5984   },
5985
5986   {
5987     Xblank,                             TRUE,   FALSE,
5988     EL_EMPTY,                           -1, -1
5989   },
5990
5991   {
5992     Xsplash_e,                          FALSE,  FALSE,
5993     EL_ACID_SPLASH_RIGHT,               -1, -1
5994   },
5995   {
5996     Xsplash_w,                          FALSE,  FALSE,
5997     EL_ACID_SPLASH_LEFT,                -1, -1
5998   },
5999
6000   {
6001     Xplant,                             TRUE,   FALSE,
6002     EL_EMC_PLANT,                       -1, -1
6003   },
6004   {
6005     Yplant,                             FALSE,  FALSE,
6006     EL_EMC_PLANT,                       -1, -1
6007   },
6008
6009   {
6010     Xacid_1,                            TRUE,   FALSE,
6011     EL_ACID,                            -1, -1
6012   },
6013   {
6014     Xacid_2,                            FALSE,  FALSE,
6015     EL_ACID,                            -1, -1
6016   },
6017   {
6018     Xacid_3,                            FALSE,  FALSE,
6019     EL_ACID,                            -1, -1
6020   },
6021   {
6022     Xacid_4,                            FALSE,  FALSE,
6023     EL_ACID,                            -1, -1
6024   },
6025   {
6026     Xacid_5,                            FALSE,  FALSE,
6027     EL_ACID,                            -1, -1
6028   },
6029   {
6030     Xacid_6,                            FALSE,  FALSE,
6031     EL_ACID,                            -1, -1
6032   },
6033   {
6034     Xacid_7,                            FALSE,  FALSE,
6035     EL_ACID,                            -1, -1
6036   },
6037   {
6038     Xacid_8,                            FALSE,  FALSE,
6039     EL_ACID,                            -1, -1
6040   },
6041
6042   {
6043     Xfake_acid_1,                       TRUE,   FALSE,
6044     EL_EMC_FAKE_ACID,                   -1, -1
6045   },
6046   {
6047     Xfake_acid_2,                       FALSE,  FALSE,
6048     EL_EMC_FAKE_ACID,                   -1, -1
6049   },
6050   {
6051     Xfake_acid_3,                       FALSE,  FALSE,
6052     EL_EMC_FAKE_ACID,                   -1, -1
6053   },
6054   {
6055     Xfake_acid_4,                       FALSE,  FALSE,
6056     EL_EMC_FAKE_ACID,                   -1, -1
6057   },
6058   {
6059     Xfake_acid_5,                       FALSE,  FALSE,
6060     EL_EMC_FAKE_ACID,                   -1, -1
6061   },
6062   {
6063     Xfake_acid_6,                       FALSE,  FALSE,
6064     EL_EMC_FAKE_ACID,                   -1, -1
6065   },
6066   {
6067     Xfake_acid_7,                       FALSE,  FALSE,
6068     EL_EMC_FAKE_ACID,                   -1, -1
6069   },
6070   {
6071     Xfake_acid_8,                       FALSE,  FALSE,
6072     EL_EMC_FAKE_ACID,                   -1, -1
6073   },
6074
6075   {
6076     Xfake_acid_1_player,                FALSE,  FALSE,
6077     EL_EMC_FAKE_ACID,                   -1, -1
6078   },
6079   {
6080     Xfake_acid_2_player,                FALSE,  FALSE,
6081     EL_EMC_FAKE_ACID,                   -1, -1
6082   },
6083   {
6084     Xfake_acid_3_player,                FALSE,  FALSE,
6085     EL_EMC_FAKE_ACID,                   -1, -1
6086   },
6087   {
6088     Xfake_acid_4_player,                FALSE,  FALSE,
6089     EL_EMC_FAKE_ACID,                   -1, -1
6090   },
6091   {
6092     Xfake_acid_5_player,                FALSE,  FALSE,
6093     EL_EMC_FAKE_ACID,                   -1, -1
6094   },
6095   {
6096     Xfake_acid_6_player,                FALSE,  FALSE,
6097     EL_EMC_FAKE_ACID,                   -1, -1
6098   },
6099   {
6100     Xfake_acid_7_player,                FALSE,  FALSE,
6101     EL_EMC_FAKE_ACID,                   -1, -1
6102   },
6103   {
6104     Xfake_acid_8_player,                FALSE,  FALSE,
6105     EL_EMC_FAKE_ACID,                   -1, -1
6106   },
6107
6108   {
6109     Xgrass,                             TRUE,   FALSE,
6110     EL_EMC_GRASS,                       -1, -1
6111   },
6112   {
6113     Ygrass_nB,                          FALSE,  FALSE,
6114     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
6115   },
6116   {
6117     Ygrass_eB,                          FALSE,  FALSE,
6118     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
6119   },
6120   {
6121     Ygrass_sB,                          FALSE,  FALSE,
6122     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
6123   },
6124   {
6125     Ygrass_wB,                          FALSE,  FALSE,
6126     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
6127   },
6128
6129   {
6130     Xdirt,                              TRUE,   FALSE,
6131     EL_SAND,                            -1, -1
6132   },
6133   {
6134     Ydirt_nB,                           FALSE,  FALSE,
6135     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
6136   },
6137   {
6138     Ydirt_eB,                           FALSE,  FALSE,
6139     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
6140   },
6141   {
6142     Ydirt_sB,                           FALSE,  FALSE,
6143     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
6144   },
6145   {
6146     Ydirt_wB,                           FALSE,  FALSE,
6147     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
6148   },
6149
6150   {
6151     Xandroid,                           TRUE,   FALSE,
6152     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
6153   },
6154   {
6155     Xandroid_1_n,                       FALSE,  FALSE,
6156     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6157   },
6158   {
6159     Xandroid_2_n,                       FALSE,  FALSE,
6160     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6161   },
6162   {
6163     Xandroid_1_e,                       FALSE,  FALSE,
6164     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6165   },
6166   {
6167     Xandroid_2_e,                       FALSE,  FALSE,
6168     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6169   },
6170   {
6171     Xandroid_1_w,                       FALSE,  FALSE,
6172     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6173   },
6174   {
6175     Xandroid_2_w,                       FALSE,  FALSE,
6176     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6177   },
6178   {
6179     Xandroid_1_s,                       FALSE,  FALSE,
6180     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6181   },
6182   {
6183     Xandroid_2_s,                       FALSE,  FALSE,
6184     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6185   },
6186   {
6187     Yandroid_n,                         FALSE,  FALSE,
6188     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6189   },
6190   {
6191     Yandroid_nB,                        FALSE,  TRUE,
6192     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6193   },
6194   {
6195     Yandroid_ne,                        FALSE,  FALSE,
6196     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
6197   },
6198   {
6199     Yandroid_neB,                       FALSE,  TRUE,
6200     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
6201   },
6202   {
6203     Yandroid_e,                         FALSE,  FALSE,
6204     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6205   },
6206   {
6207     Yandroid_eB,                        FALSE,  TRUE,
6208     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6209   },
6210   {
6211     Yandroid_se,                        FALSE,  FALSE,
6212     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
6213   },
6214   {
6215     Yandroid_seB,                       FALSE,  TRUE,
6216     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6217   },
6218   {
6219     Yandroid_s,                         FALSE,  FALSE,
6220     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6221   },
6222   {
6223     Yandroid_sB,                        FALSE,  TRUE,
6224     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6225   },
6226   {
6227     Yandroid_sw,                        FALSE,  FALSE,
6228     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
6229   },
6230   {
6231     Yandroid_swB,                       FALSE,  TRUE,
6232     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
6233   },
6234   {
6235     Yandroid_w,                         FALSE,  FALSE,
6236     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6237   },
6238   {
6239     Yandroid_wB,                        FALSE,  TRUE,
6240     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6241   },
6242   {
6243     Yandroid_nw,                        FALSE,  FALSE,
6244     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
6245   },
6246   {
6247     Yandroid_nwB,                       FALSE,  TRUE,
6248     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
6249   },
6250
6251   {
6252     Xeater_n,                           TRUE,   FALSE,
6253     EL_YAMYAM_UP,                       -1, -1
6254   },
6255   {
6256     Xeater_e,                           TRUE,   FALSE,
6257     EL_YAMYAM_RIGHT,                    -1, -1
6258   },
6259   {
6260     Xeater_w,                           TRUE,   FALSE,
6261     EL_YAMYAM_LEFT,                     -1, -1
6262   },
6263   {
6264     Xeater_s,                           TRUE,   FALSE,
6265     EL_YAMYAM_DOWN,                     -1, -1
6266   },
6267   {
6268     Yeater_n,                           FALSE,  FALSE,
6269     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6270   },
6271   {
6272     Yeater_nB,                          FALSE,  TRUE,
6273     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6274   },
6275   {
6276     Yeater_e,                           FALSE,  FALSE,
6277     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6278   },
6279   {
6280     Yeater_eB,                          FALSE,  TRUE,
6281     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6282   },
6283   {
6284     Yeater_s,                           FALSE,  FALSE,
6285     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6286   },
6287   {
6288     Yeater_sB,                          FALSE,  TRUE,
6289     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6290   },
6291   {
6292     Yeater_w,                           FALSE,  FALSE,
6293     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6294   },
6295   {
6296     Yeater_wB,                          FALSE,  TRUE,
6297     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6298   },
6299   {
6300     Yeater_stone,                       FALSE,  FALSE,
6301     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
6302   },
6303   {
6304     Yeater_spring,                      FALSE,  FALSE,
6305     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
6306   },
6307
6308   {
6309     Xalien,                             TRUE,   FALSE,
6310     EL_ROBOT,                           -1, -1
6311   },
6312   {
6313     Xalien_pause,                       FALSE,  FALSE,
6314     EL_ROBOT,                           -1, -1
6315   },
6316   {
6317     Yalien_n,                           FALSE,  FALSE,
6318     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6319   },
6320   {
6321     Yalien_nB,                          FALSE,  TRUE,
6322     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6323   },
6324   {
6325     Yalien_e,                           FALSE,  FALSE,
6326     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6327   },
6328   {
6329     Yalien_eB,                          FALSE,  TRUE,
6330     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6331   },
6332   {
6333     Yalien_s,                           FALSE,  FALSE,
6334     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6335   },
6336   {
6337     Yalien_sB,                          FALSE,  TRUE,
6338     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6339   },
6340   {
6341     Yalien_w,                           FALSE,  FALSE,
6342     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6343   },
6344   {
6345     Yalien_wB,                          FALSE,  TRUE,
6346     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6347   },
6348   {
6349     Yalien_stone,                       FALSE,  FALSE,
6350     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
6351   },
6352   {
6353     Yalien_spring,                      FALSE,  FALSE,
6354     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
6355   },
6356
6357   {
6358     Xbug_1_n,                           TRUE,   FALSE,
6359     EL_BUG_UP,                          -1, -1
6360   },
6361   {
6362     Xbug_1_e,                           TRUE,   FALSE,
6363     EL_BUG_RIGHT,                       -1, -1
6364   },
6365   {
6366     Xbug_1_s,                           TRUE,   FALSE,
6367     EL_BUG_DOWN,                        -1, -1
6368   },
6369   {
6370     Xbug_1_w,                           TRUE,   FALSE,
6371     EL_BUG_LEFT,                        -1, -1
6372   },
6373   {
6374     Xbug_2_n,                           FALSE,  FALSE,
6375     EL_BUG_UP,                          -1, -1
6376   },
6377   {
6378     Xbug_2_e,                           FALSE,  FALSE,
6379     EL_BUG_RIGHT,                       -1, -1
6380   },
6381   {
6382     Xbug_2_s,                           FALSE,  FALSE,
6383     EL_BUG_DOWN,                        -1, -1
6384   },
6385   {
6386     Xbug_2_w,                           FALSE,  FALSE,
6387     EL_BUG_LEFT,                        -1, -1
6388   },
6389   {
6390     Ybug_n,                             FALSE,  FALSE,
6391     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6392   },
6393   {
6394     Ybug_nB,                            FALSE,  TRUE,
6395     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6396   },
6397   {
6398     Ybug_e,                             FALSE,  FALSE,
6399     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6400   },
6401   {
6402     Ybug_eB,                            FALSE,  TRUE,
6403     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6404   },
6405   {
6406     Ybug_s,                             FALSE,  FALSE,
6407     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6408   },
6409   {
6410     Ybug_sB,                            FALSE,  TRUE,
6411     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6412   },
6413   {
6414     Ybug_w,                             FALSE,  FALSE,
6415     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6416   },
6417   {
6418     Ybug_wB,                            FALSE,  TRUE,
6419     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6420   },
6421   {
6422     Ybug_w_n,                           FALSE,  FALSE,
6423     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6424   },
6425   {
6426     Ybug_n_e,                           FALSE,  FALSE,
6427     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6428   },
6429   {
6430     Ybug_e_s,                           FALSE,  FALSE,
6431     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6432   },
6433   {
6434     Ybug_s_w,                           FALSE,  FALSE,
6435     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6436   },
6437   {
6438     Ybug_e_n,                           FALSE,  FALSE,
6439     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6440   },
6441   {
6442     Ybug_s_e,                           FALSE,  FALSE,
6443     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6444   },
6445   {
6446     Ybug_w_s,                           FALSE,  FALSE,
6447     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6448   },
6449   {
6450     Ybug_n_w,                           FALSE,  FALSE,
6451     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6452   },
6453   {
6454     Ybug_stone,                         FALSE,  FALSE,
6455     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
6456   },
6457   {
6458     Ybug_spring,                        FALSE,  FALSE,
6459     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
6460   },
6461
6462   {
6463     Xtank_1_n,                          TRUE,   FALSE,
6464     EL_SPACESHIP_UP,                    -1, -1
6465   },
6466   {
6467     Xtank_1_e,                          TRUE,   FALSE,
6468     EL_SPACESHIP_RIGHT,                 -1, -1
6469   },
6470   {
6471     Xtank_1_s,                          TRUE,   FALSE,
6472     EL_SPACESHIP_DOWN,                  -1, -1
6473   },
6474   {
6475     Xtank_1_w,                          TRUE,   FALSE,
6476     EL_SPACESHIP_LEFT,                  -1, -1
6477   },
6478   {
6479     Xtank_2_n,                          FALSE,  FALSE,
6480     EL_SPACESHIP_UP,                    -1, -1
6481   },
6482   {
6483     Xtank_2_e,                          FALSE,  FALSE,
6484     EL_SPACESHIP_RIGHT,                 -1, -1
6485   },
6486   {
6487     Xtank_2_s,                          FALSE,  FALSE,
6488     EL_SPACESHIP_DOWN,                  -1, -1
6489   },
6490   {
6491     Xtank_2_w,                          FALSE,  FALSE,
6492     EL_SPACESHIP_LEFT,                  -1, -1
6493   },
6494   {
6495     Ytank_n,                            FALSE,  FALSE,
6496     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6497   },
6498   {
6499     Ytank_nB,                           FALSE,  TRUE,
6500     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6501   },
6502   {
6503     Ytank_e,                            FALSE,  FALSE,
6504     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6505   },
6506   {
6507     Ytank_eB,                           FALSE,  TRUE,
6508     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6509   },
6510   {
6511     Ytank_s,                            FALSE,  FALSE,
6512     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6513   },
6514   {
6515     Ytank_sB,                           FALSE,  TRUE,
6516     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6517   },
6518   {
6519     Ytank_w,                            FALSE,  FALSE,
6520     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6521   },
6522   {
6523     Ytank_wB,                           FALSE,  TRUE,
6524     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6525   },
6526   {
6527     Ytank_w_n,                          FALSE,  FALSE,
6528     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6529   },
6530   {
6531     Ytank_n_e,                          FALSE,  FALSE,
6532     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6533   },
6534   {
6535     Ytank_e_s,                          FALSE,  FALSE,
6536     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6537   },
6538   {
6539     Ytank_s_w,                          FALSE,  FALSE,
6540     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6541   },
6542   {
6543     Ytank_e_n,                          FALSE,  FALSE,
6544     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6545   },
6546   {
6547     Ytank_s_e,                          FALSE,  FALSE,
6548     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6549   },
6550   {
6551     Ytank_w_s,                          FALSE,  FALSE,
6552     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6553   },
6554   {
6555     Ytank_n_w,                          FALSE,  FALSE,
6556     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6557   },
6558   {
6559     Ytank_stone,                        FALSE,  FALSE,
6560     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
6561   },
6562   {
6563     Ytank_spring,                       FALSE,  FALSE,
6564     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
6565   },
6566
6567   {
6568     Xemerald,                           TRUE,   FALSE,
6569     EL_EMERALD,                         -1, -1
6570   },
6571   {
6572     Xemerald_pause,                     FALSE,  FALSE,
6573     EL_EMERALD,                         -1, -1
6574   },
6575   {
6576     Xemerald_fall,                      FALSE,  FALSE,
6577     EL_EMERALD,                         -1, -1
6578   },
6579   {
6580     Xemerald_shine,                     FALSE,  FALSE,
6581     EL_EMERALD,                         ACTION_TWINKLING, -1
6582   },
6583   {
6584     Yemerald_s,                         FALSE,  FALSE,
6585     EL_EMERALD,                         ACTION_FALLING, -1
6586   },
6587   {
6588     Yemerald_sB,                        FALSE,  TRUE,
6589     EL_EMERALD,                         ACTION_FALLING, -1
6590   },
6591   {
6592     Yemerald_e,                         FALSE,  FALSE,
6593     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6594   },
6595   {
6596     Yemerald_eB,                        FALSE,  TRUE,
6597     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6598   },
6599   {
6600     Yemerald_w,                         FALSE,  FALSE,
6601     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6602   },
6603   {
6604     Yemerald_wB,                        FALSE,  TRUE,
6605     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6606   },
6607   {
6608     Yemerald_blank,                     FALSE,  FALSE,
6609     EL_EMERALD,                         ACTION_COLLECTING, -1
6610   },
6611
6612   {
6613     Xdiamond,                           TRUE,   FALSE,
6614     EL_DIAMOND,                         -1, -1
6615   },
6616   {
6617     Xdiamond_pause,                     FALSE,  FALSE,
6618     EL_DIAMOND,                         -1, -1
6619   },
6620   {
6621     Xdiamond_fall,                      FALSE,  FALSE,
6622     EL_DIAMOND,                         -1, -1
6623   },
6624   {
6625     Xdiamond_shine,                     FALSE,  FALSE,
6626     EL_DIAMOND,                         ACTION_TWINKLING, -1
6627   },
6628   {
6629     Ydiamond_s,                         FALSE,  FALSE,
6630     EL_DIAMOND,                         ACTION_FALLING, -1
6631   },
6632   {
6633     Ydiamond_sB,                        FALSE,  TRUE,
6634     EL_DIAMOND,                         ACTION_FALLING, -1
6635   },
6636   {
6637     Ydiamond_e,                         FALSE,  FALSE,
6638     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6639   },
6640   {
6641     Ydiamond_eB,                        FALSE,  TRUE,
6642     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6643   },
6644   {
6645     Ydiamond_w,                         FALSE,  FALSE,
6646     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6647   },
6648   {
6649     Ydiamond_wB,                        FALSE,  TRUE,
6650     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6651   },
6652   {
6653     Ydiamond_blank,                     FALSE,  FALSE,
6654     EL_DIAMOND,                         ACTION_COLLECTING, -1
6655   },
6656   {
6657     Ydiamond_stone,                     FALSE,  FALSE,
6658     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
6659   },
6660
6661   {
6662     Xstone,                             TRUE,   FALSE,
6663     EL_ROCK,                            -1, -1
6664   },
6665   {
6666     Xstone_pause,                       FALSE,  FALSE,
6667     EL_ROCK,                            -1, -1
6668   },
6669   {
6670     Xstone_fall,                        FALSE,  FALSE,
6671     EL_ROCK,                            -1, -1
6672   },
6673   {
6674     Ystone_s,                           FALSE,  FALSE,
6675     EL_ROCK,                            ACTION_FALLING, -1
6676   },
6677   {
6678     Ystone_sB,                          FALSE,  TRUE,
6679     EL_ROCK,                            ACTION_FALLING, -1
6680   },
6681   {
6682     Ystone_e,                           FALSE,  FALSE,
6683     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6684   },
6685   {
6686     Ystone_eB,                          FALSE,  TRUE,
6687     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6688   },
6689   {
6690     Ystone_w,                           FALSE,  FALSE,
6691     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6692   },
6693   {
6694     Ystone_wB,                          FALSE,  TRUE,
6695     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6696   },
6697
6698   {
6699     Xbomb,                              TRUE,   FALSE,
6700     EL_BOMB,                            -1, -1
6701   },
6702   {
6703     Xbomb_pause,                        FALSE,  FALSE,
6704     EL_BOMB,                            -1, -1
6705   },
6706   {
6707     Xbomb_fall,                         FALSE,  FALSE,
6708     EL_BOMB,                            -1, -1
6709   },
6710   {
6711     Ybomb_s,                            FALSE,  FALSE,
6712     EL_BOMB,                            ACTION_FALLING, -1
6713   },
6714   {
6715     Ybomb_sB,                           FALSE,  TRUE,
6716     EL_BOMB,                            ACTION_FALLING, -1
6717   },
6718   {
6719     Ybomb_e,                            FALSE,  FALSE,
6720     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6721   },
6722   {
6723     Ybomb_eB,                           FALSE,  TRUE,
6724     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6725   },
6726   {
6727     Ybomb_w,                            FALSE,  FALSE,
6728     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6729   },
6730   {
6731     Ybomb_wB,                           FALSE,  TRUE,
6732     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6733   },
6734   {
6735     Ybomb_blank,                        FALSE,  FALSE,
6736     EL_BOMB,                            ACTION_ACTIVATING, -1
6737   },
6738
6739   {
6740     Xnut,                               TRUE,   FALSE,
6741     EL_NUT,                             -1, -1
6742   },
6743   {
6744     Xnut_pause,                         FALSE,  FALSE,
6745     EL_NUT,                             -1, -1
6746   },
6747   {
6748     Xnut_fall,                          FALSE,  FALSE,
6749     EL_NUT,                             -1, -1
6750   },
6751   {
6752     Ynut_s,                             FALSE,  FALSE,
6753     EL_NUT,                             ACTION_FALLING, -1
6754   },
6755   {
6756     Ynut_sB,                            FALSE,  TRUE,
6757     EL_NUT,                             ACTION_FALLING, -1
6758   },
6759   {
6760     Ynut_e,                             FALSE,  FALSE,
6761     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6762   },
6763   {
6764     Ynut_eB,                            FALSE,  TRUE,
6765     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6766   },
6767   {
6768     Ynut_w,                             FALSE,  FALSE,
6769     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6770   },
6771   {
6772     Ynut_wB,                            FALSE,  TRUE,
6773     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6774   },
6775   {
6776     Ynut_stone,                         FALSE,  FALSE,
6777     EL_NUT,                             ACTION_BREAKING, -1
6778   },
6779
6780   {
6781     Xspring,                            TRUE,   FALSE,
6782     EL_SPRING,                          -1, -1
6783   },
6784   {
6785     Xspring_pause,                      FALSE,  FALSE,
6786     EL_SPRING,                          -1, -1
6787   },
6788   {
6789     Xspring_e,                          TRUE,   FALSE,
6790     EL_SPRING_RIGHT,                    -1, -1
6791   },
6792   {
6793     Xspring_w,                          TRUE,   FALSE,
6794     EL_SPRING_LEFT,                     -1, -1
6795   },
6796   {
6797     Xspring_fall,                       FALSE,  FALSE,
6798     EL_SPRING,                          -1, -1
6799   },
6800   {
6801     Yspring_s,                          FALSE,  FALSE,
6802     EL_SPRING,                          ACTION_FALLING, -1
6803   },
6804   {
6805     Yspring_sB,                         FALSE,  TRUE,
6806     EL_SPRING,                          ACTION_FALLING, -1
6807   },
6808   {
6809     Yspring_e,                          FALSE,  FALSE,
6810     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6811   },
6812   {
6813     Yspring_eB,                         FALSE,  TRUE,
6814     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6815   },
6816   {
6817     Yspring_w,                          FALSE,  FALSE,
6818     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6819   },
6820   {
6821     Yspring_wB,                         FALSE,  TRUE,
6822     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6823   },
6824   {
6825     Yspring_alien_e,                    FALSE,  FALSE,
6826     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6827   },
6828   {
6829     Yspring_alien_eB,                   FALSE,  TRUE,
6830     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6831   },
6832   {
6833     Yspring_alien_w,                    FALSE,  FALSE,
6834     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6835   },
6836   {
6837     Yspring_alien_wB,                   FALSE,  TRUE,
6838     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6839   },
6840
6841   {
6842     Xpush_emerald_e,                    FALSE,  FALSE,
6843     EL_EMERALD,                         -1, MV_BIT_RIGHT
6844   },
6845   {
6846     Xpush_emerald_w,                    FALSE,  FALSE,
6847     EL_EMERALD,                         -1, MV_BIT_LEFT
6848   },
6849   {
6850     Xpush_diamond_e,                    FALSE,  FALSE,
6851     EL_DIAMOND,                         -1, MV_BIT_RIGHT
6852   },
6853   {
6854     Xpush_diamond_w,                    FALSE,  FALSE,
6855     EL_DIAMOND,                         -1, MV_BIT_LEFT
6856   },
6857   {
6858     Xpush_stone_e,                      FALSE,  FALSE,
6859     EL_ROCK,                            -1, MV_BIT_RIGHT
6860   },
6861   {
6862     Xpush_stone_w,                      FALSE,  FALSE,
6863     EL_ROCK,                            -1, MV_BIT_LEFT
6864   },
6865   {
6866     Xpush_bomb_e,                       FALSE,  FALSE,
6867     EL_BOMB,                            -1, MV_BIT_RIGHT
6868   },
6869   {
6870     Xpush_bomb_w,                       FALSE,  FALSE,
6871     EL_BOMB,                            -1, MV_BIT_LEFT
6872   },
6873   {
6874     Xpush_nut_e,                        FALSE,  FALSE,
6875     EL_NUT,                             -1, MV_BIT_RIGHT
6876   },
6877   {
6878     Xpush_nut_w,                        FALSE,  FALSE,
6879     EL_NUT,                             -1, MV_BIT_LEFT
6880   },
6881   {
6882     Xpush_spring_e,                     FALSE,  FALSE,
6883     EL_SPRING_RIGHT,                    -1, MV_BIT_RIGHT
6884   },
6885   {
6886     Xpush_spring_w,                     FALSE,  FALSE,
6887     EL_SPRING_LEFT,                     -1, MV_BIT_LEFT
6888   },
6889
6890   {
6891     Xdynamite,                          TRUE,   FALSE,
6892     EL_EM_DYNAMITE,                     -1, -1
6893   },
6894   {
6895     Ydynamite_blank,                    FALSE,  FALSE,
6896     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6897   },
6898   {
6899     Xdynamite_1,                        TRUE,   FALSE,
6900     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6901   },
6902   {
6903     Xdynamite_2,                        FALSE,  FALSE,
6904     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6905   },
6906   {
6907     Xdynamite_3,                        FALSE,  FALSE,
6908     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6909   },
6910   {
6911     Xdynamite_4,                        FALSE,  FALSE,
6912     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6913   },
6914
6915   {
6916     Xkey_1,                             TRUE,   FALSE,
6917     EL_EM_KEY_1,                        -1, -1
6918   },
6919   {
6920     Xkey_2,                             TRUE,   FALSE,
6921     EL_EM_KEY_2,                        -1, -1
6922   },
6923   {
6924     Xkey_3,                             TRUE,   FALSE,
6925     EL_EM_KEY_3,                        -1, -1
6926   },
6927   {
6928     Xkey_4,                             TRUE,   FALSE,
6929     EL_EM_KEY_4,                        -1, -1
6930   },
6931   {
6932     Xkey_5,                             TRUE,   FALSE,
6933     EL_EMC_KEY_5,                       -1, -1
6934   },
6935   {
6936     Xkey_6,                             TRUE,   FALSE,
6937     EL_EMC_KEY_6,                       -1, -1
6938   },
6939   {
6940     Xkey_7,                             TRUE,   FALSE,
6941     EL_EMC_KEY_7,                       -1, -1
6942   },
6943   {
6944     Xkey_8,                             TRUE,   FALSE,
6945     EL_EMC_KEY_8,                       -1, -1
6946   },
6947
6948   {
6949     Xdoor_1,                            TRUE,   FALSE,
6950     EL_EM_GATE_1,                       -1, -1
6951   },
6952   {
6953     Xdoor_2,                            TRUE,   FALSE,
6954     EL_EM_GATE_2,                       -1, -1
6955   },
6956   {
6957     Xdoor_3,                            TRUE,   FALSE,
6958     EL_EM_GATE_3,                       -1, -1
6959   },
6960   {
6961     Xdoor_4,                            TRUE,   FALSE,
6962     EL_EM_GATE_4,                       -1, -1
6963   },
6964   {
6965     Xdoor_5,                            TRUE,   FALSE,
6966     EL_EMC_GATE_5,                      -1, -1
6967   },
6968   {
6969     Xdoor_6,                            TRUE,   FALSE,
6970     EL_EMC_GATE_6,                      -1, -1
6971   },
6972   {
6973     Xdoor_7,                            TRUE,   FALSE,
6974     EL_EMC_GATE_7,                      -1, -1
6975   },
6976   {
6977     Xdoor_8,                            TRUE,   FALSE,
6978     EL_EMC_GATE_8,                      -1, -1
6979   },
6980
6981   {
6982     Xfake_door_1,                       TRUE,   FALSE,
6983     EL_EM_GATE_1_GRAY,                  -1, -1
6984   },
6985   {
6986     Xfake_door_2,                       TRUE,   FALSE,
6987     EL_EM_GATE_2_GRAY,                  -1, -1
6988   },
6989   {
6990     Xfake_door_3,                       TRUE,   FALSE,
6991     EL_EM_GATE_3_GRAY,                  -1, -1
6992   },
6993   {
6994     Xfake_door_4,                       TRUE,   FALSE,
6995     EL_EM_GATE_4_GRAY,                  -1, -1
6996   },
6997   {
6998     Xfake_door_5,                       TRUE,   FALSE,
6999     EL_EMC_GATE_5_GRAY,                 -1, -1
7000   },
7001   {
7002     Xfake_door_6,                       TRUE,   FALSE,
7003     EL_EMC_GATE_6_GRAY,                 -1, -1
7004   },
7005   {
7006     Xfake_door_7,                       TRUE,   FALSE,
7007     EL_EMC_GATE_7_GRAY,                 -1, -1
7008   },
7009   {
7010     Xfake_door_8,                       TRUE,   FALSE,
7011     EL_EMC_GATE_8_GRAY,                 -1, -1
7012   },
7013
7014   {
7015     Xballoon,                           TRUE,   FALSE,
7016     EL_BALLOON,                         -1, -1
7017   },
7018   {
7019     Yballoon_n,                         FALSE,  FALSE,
7020     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
7021   },
7022   {
7023     Yballoon_nB,                        FALSE,  TRUE,
7024     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
7025   },
7026   {
7027     Yballoon_e,                         FALSE,  FALSE,
7028     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
7029   },
7030   {
7031     Yballoon_eB,                        FALSE,  TRUE,
7032     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
7033   },
7034   {
7035     Yballoon_s,                         FALSE,  FALSE,
7036     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
7037   },
7038   {
7039     Yballoon_sB,                        FALSE,  TRUE,
7040     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
7041   },
7042   {
7043     Yballoon_w,                         FALSE,  FALSE,
7044     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
7045   },
7046   {
7047     Yballoon_wB,                        FALSE,  TRUE,
7048     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
7049   },
7050
7051   {
7052     Xball_1,                            TRUE,   FALSE,
7053     EL_EMC_MAGIC_BALL,                  -1, -1
7054   },
7055   {
7056     Yball_1,                            FALSE,  FALSE,
7057     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7058   },
7059   {
7060     Xball_2,                            FALSE,  FALSE,
7061     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7062   },
7063   {
7064     Yball_2,                            FALSE,  FALSE,
7065     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7066   },
7067   {
7068     Yball_blank,                        FALSE,  FALSE,
7069     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
7070   },
7071
7072   {
7073     Xamoeba_1,                          TRUE,   FALSE,
7074     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7075   },
7076   {
7077     Xamoeba_2,                          FALSE,  FALSE,
7078     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7079   },
7080   {
7081     Xamoeba_3,                          FALSE,  FALSE,
7082     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7083   },
7084   {
7085     Xamoeba_4,                          FALSE,  FALSE,
7086     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7087   },
7088   {
7089     Xamoeba_5,                          TRUE,   FALSE,
7090     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7091   },
7092   {
7093     Xamoeba_6,                          FALSE,  FALSE,
7094     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7095   },
7096   {
7097     Xamoeba_7,                          FALSE,  FALSE,
7098     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7099   },
7100   {
7101     Xamoeba_8,                          FALSE,  FALSE,
7102     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7103   },
7104
7105   {
7106     Xdrip,                              TRUE,   FALSE,
7107     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
7108   },
7109   {
7110     Xdrip_fall,                         FALSE,  FALSE,
7111     EL_AMOEBA_DROP,                     -1, -1
7112   },
7113   {
7114     Xdrip_stretch,                      FALSE,  FALSE,
7115     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7116   },
7117   {
7118     Xdrip_stretchB,                     FALSE,  TRUE,
7119     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7120   },
7121   {
7122     Ydrip_1_s,                          FALSE,  FALSE,
7123     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7124   },
7125   {
7126     Ydrip_1_sB,                         FALSE,  TRUE,
7127     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7128   },
7129   {
7130     Ydrip_2_s,                          FALSE,  FALSE,
7131     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7132   },
7133   {
7134     Ydrip_2_sB,                         FALSE,  TRUE,
7135     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7136   },
7137
7138   {
7139     Xwonderwall,                        TRUE,   FALSE,
7140     EL_MAGIC_WALL,                      -1, -1
7141   },
7142   {
7143     Ywonderwall,                        FALSE,  FALSE,
7144     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
7145   },
7146
7147   {
7148     Xwheel,                             TRUE,   FALSE,
7149     EL_ROBOT_WHEEL,                     -1, -1
7150   },
7151   {
7152     Ywheel,                             FALSE,  FALSE,
7153     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
7154   },
7155
7156   {
7157     Xswitch,                            TRUE,   FALSE,
7158     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
7159   },
7160   {
7161     Yswitch,                            FALSE,  FALSE,
7162     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
7163   },
7164
7165   {
7166     Xbumper,                            TRUE,   FALSE,
7167     EL_EMC_SPRING_BUMPER,               -1, -1
7168   },
7169   {
7170     Ybumper,                            FALSE,  FALSE,
7171     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
7172   },
7173
7174   {
7175     Xacid_nw,                           TRUE,   FALSE,
7176     EL_ACID_POOL_TOPLEFT,               -1, -1
7177   },
7178   {
7179     Xacid_ne,                           TRUE,   FALSE,
7180     EL_ACID_POOL_TOPRIGHT,              -1, -1
7181   },
7182   {
7183     Xacid_sw,                           TRUE,   FALSE,
7184     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
7185   },
7186   {
7187     Xacid_s,                            TRUE,   FALSE,
7188     EL_ACID_POOL_BOTTOM,                -1, -1
7189   },
7190   {
7191     Xacid_se,                           TRUE,   FALSE,
7192     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
7193   },
7194
7195   {
7196     Xfake_blank,                        TRUE,   FALSE,
7197     EL_INVISIBLE_WALL,                  -1, -1
7198   },
7199   {
7200     Yfake_blank,                        FALSE,  FALSE,
7201     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
7202   },
7203
7204   {
7205     Xfake_grass,                        TRUE,   FALSE,
7206     EL_EMC_FAKE_GRASS,                  -1, -1
7207   },
7208   {
7209     Yfake_grass,                        FALSE,  FALSE,
7210     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
7211   },
7212
7213   {
7214     Xfake_amoeba,                       TRUE,   FALSE,
7215     EL_EMC_DRIPPER,                     -1, -1
7216   },
7217   {
7218     Yfake_amoeba,                       FALSE,  FALSE,
7219     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
7220   },
7221
7222   {
7223     Xlenses,                            TRUE,   FALSE,
7224     EL_EMC_LENSES,                      -1, -1
7225   },
7226
7227   {
7228     Xmagnify,                           TRUE,   FALSE,
7229     EL_EMC_MAGNIFIER,                   -1, -1
7230   },
7231
7232   {
7233     Xsand,                              TRUE,   FALSE,
7234     EL_QUICKSAND_EMPTY,                 -1, -1
7235   },
7236   {
7237     Xsand_stone,                        TRUE,   FALSE,
7238     EL_QUICKSAND_FULL,                  -1, -1
7239   },
7240   {
7241     Xsand_stonein_1,                    FALSE,  TRUE,
7242     EL_ROCK,                            ACTION_FILLING, -1
7243   },
7244   {
7245     Xsand_stonein_2,                    FALSE,  TRUE,
7246     EL_ROCK,                            ACTION_FILLING, -1
7247   },
7248   {
7249     Xsand_stonein_3,                    FALSE,  TRUE,
7250     EL_ROCK,                            ACTION_FILLING, -1
7251   },
7252   {
7253     Xsand_stonein_4,                    FALSE,  TRUE,
7254     EL_ROCK,                            ACTION_FILLING, -1
7255   },
7256   {
7257     Xsand_sandstone_1,                  FALSE,  FALSE,
7258     EL_QUICKSAND_FILLING,               -1, -1
7259   },
7260   {
7261     Xsand_sandstone_2,                  FALSE,  FALSE,
7262     EL_QUICKSAND_FILLING,               -1, -1
7263   },
7264   {
7265     Xsand_sandstone_3,                  FALSE,  FALSE,
7266     EL_QUICKSAND_FILLING,               -1, -1
7267   },
7268   {
7269     Xsand_sandstone_4,                  FALSE,  FALSE,
7270     EL_QUICKSAND_FILLING,               -1, -1
7271   },
7272   {
7273     Xsand_stonesand_1,                  FALSE,  FALSE,
7274     EL_QUICKSAND_EMPTYING,              -1, -1
7275   },
7276   {
7277     Xsand_stonesand_2,                  FALSE,  FALSE,
7278     EL_QUICKSAND_EMPTYING,              -1, -1
7279   },
7280   {
7281     Xsand_stonesand_3,                  FALSE,  FALSE,
7282     EL_QUICKSAND_EMPTYING,              -1, -1
7283   },
7284   {
7285     Xsand_stonesand_4,                  FALSE,  FALSE,
7286     EL_QUICKSAND_EMPTYING,              -1, -1
7287   },
7288   {
7289     Xsand_stoneout_1,                   FALSE,  FALSE,
7290     EL_ROCK,                            ACTION_EMPTYING, -1
7291   },
7292   {
7293     Xsand_stoneout_2,                   FALSE,  FALSE,
7294     EL_ROCK,                            ACTION_EMPTYING, -1
7295   },
7296   {
7297     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
7298     EL_QUICKSAND_EMPTYING,              -1, -1
7299   },
7300   {
7301     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
7302     EL_QUICKSAND_EMPTYING,              -1, -1
7303   },
7304
7305   {
7306     Xslide_ns,                          TRUE,   FALSE,
7307     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
7308   },
7309   {
7310     Yslide_ns_blank,                    FALSE,  FALSE,
7311     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
7312   },
7313   {
7314     Xslide_ew,                          TRUE,   FALSE,
7315     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
7316   },
7317   {
7318     Yslide_ew_blank,                    FALSE,  FALSE,
7319     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
7320   },
7321
7322   {
7323     Xwind_n,                            TRUE,   FALSE,
7324     EL_BALLOON_SWITCH_UP,               -1, -1
7325   },
7326   {
7327     Xwind_e,                            TRUE,   FALSE,
7328     EL_BALLOON_SWITCH_RIGHT,            -1, -1
7329   },
7330   {
7331     Xwind_s,                            TRUE,   FALSE,
7332     EL_BALLOON_SWITCH_DOWN,             -1, -1
7333   },
7334   {
7335     Xwind_w,                            TRUE,   FALSE,
7336     EL_BALLOON_SWITCH_LEFT,             -1, -1
7337   },
7338   {
7339     Xwind_any,                          TRUE,   FALSE,
7340     EL_BALLOON_SWITCH_ANY,              -1, -1
7341   },
7342   {
7343     Xwind_stop,                         TRUE,   FALSE,
7344     EL_BALLOON_SWITCH_NONE,             -1, -1
7345   },
7346
7347   {
7348     Xexit,                              TRUE,   FALSE,
7349     EL_EM_EXIT_CLOSED,                  -1, -1
7350   },
7351   {
7352     Xexit_1,                            TRUE,   FALSE,
7353     EL_EM_EXIT_OPEN,                    -1, -1
7354   },
7355   {
7356     Xexit_2,                            FALSE,  FALSE,
7357     EL_EM_EXIT_OPEN,                    -1, -1
7358   },
7359   {
7360     Xexit_3,                            FALSE,  FALSE,
7361     EL_EM_EXIT_OPEN,                    -1, -1
7362   },
7363
7364   {
7365     Xpause,                             FALSE,  FALSE,
7366     EL_EMPTY,                           -1, -1
7367   },
7368
7369   {
7370     Xwall_1,                            TRUE,   FALSE,
7371     EL_WALL,                            -1, -1
7372   },
7373   {
7374     Xwall_2,                            TRUE,   FALSE,
7375     EL_EMC_WALL_14,                     -1, -1
7376   },
7377   {
7378     Xwall_3,                            TRUE,   FALSE,
7379     EL_EMC_WALL_15,                     -1, -1
7380   },
7381   {
7382     Xwall_4,                            TRUE,   FALSE,
7383     EL_EMC_WALL_16,                     -1, -1
7384   },
7385
7386   {
7387     Xroundwall_1,                       TRUE,   FALSE,
7388     EL_WALL_SLIPPERY,                   -1, -1
7389   },
7390   {
7391     Xroundwall_2,                       TRUE,   FALSE,
7392     EL_EMC_WALL_SLIPPERY_2,             -1, -1
7393   },
7394   {
7395     Xroundwall_3,                       TRUE,   FALSE,
7396     EL_EMC_WALL_SLIPPERY_3,             -1, -1
7397   },
7398   {
7399     Xroundwall_4,                       TRUE,   FALSE,
7400     EL_EMC_WALL_SLIPPERY_4,             -1, -1
7401   },
7402
7403   {
7404     Xsteel_1,                           TRUE,   FALSE,
7405     EL_STEELWALL,                       -1, -1
7406   },
7407   {
7408     Xsteel_2,                           TRUE,   FALSE,
7409     EL_EMC_STEELWALL_2,                 -1, -1
7410   },
7411   {
7412     Xsteel_3,                           TRUE,   FALSE,
7413     EL_EMC_STEELWALL_3,                 -1, -1
7414   },
7415   {
7416     Xsteel_4,                           TRUE,   FALSE,
7417     EL_EMC_STEELWALL_4,                 -1, -1
7418   },
7419
7420   {
7421     Xdecor_1,                           TRUE,   FALSE,
7422     EL_EMC_WALL_8,                      -1, -1
7423   },
7424   {
7425     Xdecor_2,                           TRUE,   FALSE,
7426     EL_EMC_WALL_6,                      -1, -1
7427   },
7428   {
7429     Xdecor_3,                           TRUE,   FALSE,
7430     EL_EMC_WALL_4,                      -1, -1
7431   },
7432   {
7433     Xdecor_4,                           TRUE,   FALSE,
7434     EL_EMC_WALL_7,                      -1, -1
7435   },
7436   {
7437     Xdecor_5,                           TRUE,   FALSE,
7438     EL_EMC_WALL_5,                      -1, -1
7439   },
7440   {
7441     Xdecor_6,                           TRUE,   FALSE,
7442     EL_EMC_WALL_9,                      -1, -1
7443   },
7444   {
7445     Xdecor_7,                           TRUE,   FALSE,
7446     EL_EMC_WALL_10,                     -1, -1
7447   },
7448   {
7449     Xdecor_8,                           TRUE,   FALSE,
7450     EL_EMC_WALL_1,                      -1, -1
7451   },
7452   {
7453     Xdecor_9,                           TRUE,   FALSE,
7454     EL_EMC_WALL_2,                      -1, -1
7455   },
7456   {
7457     Xdecor_10,                          TRUE,   FALSE,
7458     EL_EMC_WALL_3,                      -1, -1
7459   },
7460   {
7461     Xdecor_11,                          TRUE,   FALSE,
7462     EL_EMC_WALL_11,                     -1, -1
7463   },
7464   {
7465     Xdecor_12,                          TRUE,   FALSE,
7466     EL_EMC_WALL_12,                     -1, -1
7467   },
7468
7469   {
7470     Xalpha_0,                           TRUE,   FALSE,
7471     EL_CHAR('0'),                       -1, -1
7472   },
7473   {
7474     Xalpha_1,                           TRUE,   FALSE,
7475     EL_CHAR('1'),                       -1, -1
7476   },
7477   {
7478     Xalpha_2,                           TRUE,   FALSE,
7479     EL_CHAR('2'),                       -1, -1
7480   },
7481   {
7482     Xalpha_3,                           TRUE,   FALSE,
7483     EL_CHAR('3'),                       -1, -1
7484   },
7485   {
7486     Xalpha_4,                           TRUE,   FALSE,
7487     EL_CHAR('4'),                       -1, -1
7488   },
7489   {
7490     Xalpha_5,                           TRUE,   FALSE,
7491     EL_CHAR('5'),                       -1, -1
7492   },
7493   {
7494     Xalpha_6,                           TRUE,   FALSE,
7495     EL_CHAR('6'),                       -1, -1
7496   },
7497   {
7498     Xalpha_7,                           TRUE,   FALSE,
7499     EL_CHAR('7'),                       -1, -1
7500   },
7501   {
7502     Xalpha_8,                           TRUE,   FALSE,
7503     EL_CHAR('8'),                       -1, -1
7504   },
7505   {
7506     Xalpha_9,                           TRUE,   FALSE,
7507     EL_CHAR('9'),                       -1, -1
7508   },
7509   {
7510     Xalpha_excla,                       TRUE,   FALSE,
7511     EL_CHAR('!'),                       -1, -1
7512   },
7513   {
7514     Xalpha_apost,                       TRUE,   FALSE,
7515     EL_CHAR('\''),                      -1, -1
7516   },
7517   {
7518     Xalpha_comma,                       TRUE,   FALSE,
7519     EL_CHAR(','),                       -1, -1
7520   },
7521   {
7522     Xalpha_minus,                       TRUE,   FALSE,
7523     EL_CHAR('-'),                       -1, -1
7524   },
7525   {
7526     Xalpha_perio,                       TRUE,   FALSE,
7527     EL_CHAR('.'),                       -1, -1
7528   },
7529   {
7530     Xalpha_colon,                       TRUE,   FALSE,
7531     EL_CHAR(':'),                       -1, -1
7532   },
7533   {
7534     Xalpha_quest,                       TRUE,   FALSE,
7535     EL_CHAR('?'),                       -1, -1
7536   },
7537   {
7538     Xalpha_a,                           TRUE,   FALSE,
7539     EL_CHAR('A'),                       -1, -1
7540   },
7541   {
7542     Xalpha_b,                           TRUE,   FALSE,
7543     EL_CHAR('B'),                       -1, -1
7544   },
7545   {
7546     Xalpha_c,                           TRUE,   FALSE,
7547     EL_CHAR('C'),                       -1, -1
7548   },
7549   {
7550     Xalpha_d,                           TRUE,   FALSE,
7551     EL_CHAR('D'),                       -1, -1
7552   },
7553   {
7554     Xalpha_e,                           TRUE,   FALSE,
7555     EL_CHAR('E'),                       -1, -1
7556   },
7557   {
7558     Xalpha_f,                           TRUE,   FALSE,
7559     EL_CHAR('F'),                       -1, -1
7560   },
7561   {
7562     Xalpha_g,                           TRUE,   FALSE,
7563     EL_CHAR('G'),                       -1, -1
7564   },
7565   {
7566     Xalpha_h,                           TRUE,   FALSE,
7567     EL_CHAR('H'),                       -1, -1
7568   },
7569   {
7570     Xalpha_i,                           TRUE,   FALSE,
7571     EL_CHAR('I'),                       -1, -1
7572   },
7573   {
7574     Xalpha_j,                           TRUE,   FALSE,
7575     EL_CHAR('J'),                       -1, -1
7576   },
7577   {
7578     Xalpha_k,                           TRUE,   FALSE,
7579     EL_CHAR('K'),                       -1, -1
7580   },
7581   {
7582     Xalpha_l,                           TRUE,   FALSE,
7583     EL_CHAR('L'),                       -1, -1
7584   },
7585   {
7586     Xalpha_m,                           TRUE,   FALSE,
7587     EL_CHAR('M'),                       -1, -1
7588   },
7589   {
7590     Xalpha_n,                           TRUE,   FALSE,
7591     EL_CHAR('N'),                       -1, -1
7592   },
7593   {
7594     Xalpha_o,                           TRUE,   FALSE,
7595     EL_CHAR('O'),                       -1, -1
7596   },
7597   {
7598     Xalpha_p,                           TRUE,   FALSE,
7599     EL_CHAR('P'),                       -1, -1
7600   },
7601   {
7602     Xalpha_q,                           TRUE,   FALSE,
7603     EL_CHAR('Q'),                       -1, -1
7604   },
7605   {
7606     Xalpha_r,                           TRUE,   FALSE,
7607     EL_CHAR('R'),                       -1, -1
7608   },
7609   {
7610     Xalpha_s,                           TRUE,   FALSE,
7611     EL_CHAR('S'),                       -1, -1
7612   },
7613   {
7614     Xalpha_t,                           TRUE,   FALSE,
7615     EL_CHAR('T'),                       -1, -1
7616   },
7617   {
7618     Xalpha_u,                           TRUE,   FALSE,
7619     EL_CHAR('U'),                       -1, -1
7620   },
7621   {
7622     Xalpha_v,                           TRUE,   FALSE,
7623     EL_CHAR('V'),                       -1, -1
7624   },
7625   {
7626     Xalpha_w,                           TRUE,   FALSE,
7627     EL_CHAR('W'),                       -1, -1
7628   },
7629   {
7630     Xalpha_x,                           TRUE,   FALSE,
7631     EL_CHAR('X'),                       -1, -1
7632   },
7633   {
7634     Xalpha_y,                           TRUE,   FALSE,
7635     EL_CHAR('Y'),                       -1, -1
7636   },
7637   {
7638     Xalpha_z,                           TRUE,   FALSE,
7639     EL_CHAR('Z'),                       -1, -1
7640   },
7641   {
7642     Xalpha_arrow_e,                     TRUE,   FALSE,
7643     EL_CHAR('>'),                       -1, -1
7644   },
7645   {
7646     Xalpha_arrow_w,                     TRUE,   FALSE,
7647     EL_CHAR('<'),                       -1, -1
7648   },
7649   {
7650     Xalpha_copyr,                       TRUE,   FALSE,
7651     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
7652   },
7653
7654   {
7655     Ykey_1_blank,                       FALSE,  FALSE,
7656     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
7657   },
7658   {
7659     Ykey_2_blank,                       FALSE,  FALSE,
7660     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
7661   },
7662   {
7663     Ykey_3_blank,                       FALSE,  FALSE,
7664     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
7665   },
7666   {
7667     Ykey_4_blank,                       FALSE,  FALSE,
7668     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
7669   },
7670   {
7671     Ykey_5_blank,                       FALSE,  FALSE,
7672     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
7673   },
7674   {
7675     Ykey_6_blank,                       FALSE,  FALSE,
7676     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
7677   },
7678   {
7679     Ykey_7_blank,                       FALSE,  FALSE,
7680     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
7681   },
7682   {
7683     Ykey_8_blank,                       FALSE,  FALSE,
7684     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
7685   },
7686   {
7687     Ylenses_blank,                      FALSE,  FALSE,
7688     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
7689   },
7690   {
7691     Ymagnify_blank,                     FALSE,  FALSE,
7692     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
7693   },
7694   {
7695     Ygrass_blank,                       FALSE,  FALSE,
7696     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
7697   },
7698   {
7699     Ydirt_blank,                        FALSE,  FALSE,
7700     EL_SAND,                            ACTION_SNAPPING, -1
7701   },
7702
7703   {
7704     -1,                                 FALSE,  FALSE,
7705     -1,                                 -1, -1
7706   }
7707 };
7708
7709 static struct Mapping_EM_to_RND_player
7710 {
7711   int action_em;
7712   int player_nr;
7713
7714   int element_rnd;
7715   int action;
7716   int direction;
7717 }
7718 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7719 {
7720   {
7721     PLY_walk_n,                         0,
7722     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
7723   },
7724   {
7725     PLY_walk_e,                         0,
7726     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
7727   },
7728   {
7729     PLY_walk_s,                         0,
7730     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
7731   },
7732   {
7733     PLY_walk_w,                         0,
7734     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
7735   },
7736   {
7737     PLY_push_n,                         0,
7738     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
7739   },
7740   {
7741     PLY_push_e,                         0,
7742     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
7743   },
7744   {
7745     PLY_push_s,                         0,
7746     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
7747   },
7748   {
7749     PLY_push_w,                         0,
7750     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
7751   },
7752   {
7753     PLY_shoot_n,                        0,
7754     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
7755   },
7756   {
7757     PLY_shoot_e,                        0,
7758     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7759   },
7760   {
7761     PLY_shoot_s,                        0,
7762     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
7763   },
7764   {
7765     PLY_shoot_w,                        0,
7766     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
7767   },
7768   {
7769     PLY_walk_n,                         1,
7770     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
7771   },
7772   {
7773     PLY_walk_e,                         1,
7774     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
7775   },
7776   {
7777     PLY_walk_s,                         1,
7778     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
7779   },
7780   {
7781     PLY_walk_w,                         1,
7782     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
7783   },
7784   {
7785     PLY_push_n,                         1,
7786     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
7787   },
7788   {
7789     PLY_push_e,                         1,
7790     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
7791   },
7792   {
7793     PLY_push_s,                         1,
7794     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
7795   },
7796   {
7797     PLY_push_w,                         1,
7798     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
7799   },
7800   {
7801     PLY_shoot_n,                        1,
7802     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
7803   },
7804   {
7805     PLY_shoot_e,                        1,
7806     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7807   },
7808   {
7809     PLY_shoot_s,                        1,
7810     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
7811   },
7812   {
7813     PLY_shoot_w,                        1,
7814     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
7815   },
7816   {
7817     PLY_still,                          0,
7818     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
7819   },
7820   {
7821     PLY_still,                          1,
7822     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
7823   },
7824   {
7825     PLY_walk_n,                         2,
7826     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
7827   },
7828   {
7829     PLY_walk_e,                         2,
7830     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
7831   },
7832   {
7833     PLY_walk_s,                         2,
7834     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
7835   },
7836   {
7837     PLY_walk_w,                         2,
7838     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
7839   },
7840   {
7841     PLY_push_n,                         2,
7842     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
7843   },
7844   {
7845     PLY_push_e,                         2,
7846     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
7847   },
7848   {
7849     PLY_push_s,                         2,
7850     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
7851   },
7852   {
7853     PLY_push_w,                         2,
7854     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
7855   },
7856   {
7857     PLY_shoot_n,                        2,
7858     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
7859   },
7860   {
7861     PLY_shoot_e,                        2,
7862     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7863   },
7864   {
7865     PLY_shoot_s,                        2,
7866     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
7867   },
7868   {
7869     PLY_shoot_w,                        2,
7870     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
7871   },
7872   {
7873     PLY_walk_n,                         3,
7874     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
7875   },
7876   {
7877     PLY_walk_e,                         3,
7878     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
7879   },
7880   {
7881     PLY_walk_s,                         3,
7882     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
7883   },
7884   {
7885     PLY_walk_w,                         3,
7886     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
7887   },
7888   {
7889     PLY_push_n,                         3,
7890     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
7891   },
7892   {
7893     PLY_push_e,                         3,
7894     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
7895   },
7896   {
7897     PLY_push_s,                         3,
7898     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
7899   },
7900   {
7901     PLY_push_w,                         3,
7902     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
7903   },
7904   {
7905     PLY_shoot_n,                        3,
7906     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
7907   },
7908   {
7909     PLY_shoot_e,                        3,
7910     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7911   },
7912   {
7913     PLY_shoot_s,                        3,
7914     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
7915   },
7916   {
7917     PLY_shoot_w,                        3,
7918     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
7919   },
7920   {
7921     PLY_still,                          2,
7922     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
7923   },
7924   {
7925     PLY_still,                          3,
7926     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7927   },
7928
7929   {
7930     -1,                                 -1,
7931     -1,                                 -1, -1
7932   }
7933 };
7934
7935 int map_element_RND_to_EM_cave(int element_rnd)
7936 {
7937   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7938   static boolean mapping_initialized = FALSE;
7939
7940   if (!mapping_initialized)
7941   {
7942     int i;
7943
7944     // return "Xalpha_quest" for all undefined elements in mapping array
7945     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7946       mapping_RND_to_EM[i] = Xalpha_quest;
7947
7948     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7949       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7950         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7951           em_object_mapping_list[i].element_em;
7952
7953     mapping_initialized = TRUE;
7954   }
7955
7956   if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7957   {
7958     Warn("invalid RND level element %d", element_rnd);
7959
7960     return EL_UNKNOWN;
7961   }
7962
7963   return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7964 }
7965
7966 int map_element_EM_to_RND_cave(int element_em_cave)
7967 {
7968   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7969   static boolean mapping_initialized = FALSE;
7970
7971   if (!mapping_initialized)
7972   {
7973     int i;
7974
7975     // return "EL_UNKNOWN" for all undefined elements in mapping array
7976     for (i = 0; i < GAME_TILE_MAX; i++)
7977       mapping_EM_to_RND[i] = EL_UNKNOWN;
7978
7979     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7980       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7981         em_object_mapping_list[i].element_rnd;
7982
7983     mapping_initialized = TRUE;
7984   }
7985
7986   if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7987   {
7988     Warn("invalid EM cave element %d", element_em_cave);
7989
7990     return EL_UNKNOWN;
7991   }
7992
7993   return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7994 }
7995
7996 int map_element_EM_to_RND_game(int element_em_game)
7997 {
7998   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7999   static boolean mapping_initialized = FALSE;
8000
8001   if (!mapping_initialized)
8002   {
8003     int i;
8004
8005     // return "EL_UNKNOWN" for all undefined elements in mapping array
8006     for (i = 0; i < GAME_TILE_MAX; i++)
8007       mapping_EM_to_RND[i] = EL_UNKNOWN;
8008
8009     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8010       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8011         em_object_mapping_list[i].element_rnd;
8012
8013     mapping_initialized = TRUE;
8014   }
8015
8016   if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8017   {
8018     Warn("invalid EM game element %d", element_em_game);
8019
8020     return EL_UNKNOWN;
8021   }
8022
8023   return mapping_EM_to_RND[element_em_game];
8024 }
8025
8026 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8027 {
8028   struct LevelInfo_EM *level_em = level->native_em_level;
8029   struct CAVE *cav = level_em->cav;
8030   int i, j;
8031
8032   for (i = 0; i < GAME_TILE_MAX; i++)
8033     cav->android_array[i] = Cblank;
8034
8035   for (i = 0; i < level->num_android_clone_elements; i++)
8036   {
8037     int element_rnd = level->android_clone_element[i];
8038     int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8039
8040     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8041       if (em_object_mapping_list[j].element_rnd == element_rnd)
8042         cav->android_array[em_object_mapping_list[j].element_em] =
8043           element_em_cave;
8044   }
8045 }
8046
8047 void map_android_clone_elements_EM_to_RND(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   level->num_android_clone_elements = 0;
8054
8055   for (i = 0; i < GAME_TILE_MAX; i++)
8056   {
8057     int element_em_cave = cav->android_array[i];
8058     int element_rnd;
8059     boolean element_found = FALSE;
8060
8061     if (element_em_cave == Cblank)
8062       continue;
8063
8064     element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8065
8066     for (j = 0; j < level->num_android_clone_elements; j++)
8067       if (level->android_clone_element[j] == element_rnd)
8068         element_found = TRUE;
8069
8070     if (!element_found)
8071     {
8072       level->android_clone_element[level->num_android_clone_elements++] =
8073         element_rnd;
8074
8075       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8076         break;
8077     }
8078   }
8079
8080   if (level->num_android_clone_elements == 0)
8081   {
8082     level->num_android_clone_elements = 1;
8083     level->android_clone_element[0] = EL_EMPTY;
8084   }
8085 }
8086
8087 int map_direction_RND_to_EM(int direction)
8088 {
8089   return (direction == MV_UP    ? 0 :
8090           direction == MV_RIGHT ? 1 :
8091           direction == MV_DOWN  ? 2 :
8092           direction == MV_LEFT  ? 3 :
8093           -1);
8094 }
8095
8096 int map_direction_EM_to_RND(int direction)
8097 {
8098   return (direction == 0 ? MV_UP    :
8099           direction == 1 ? MV_RIGHT :
8100           direction == 2 ? MV_DOWN  :
8101           direction == 3 ? MV_LEFT  :
8102           MV_NONE);
8103 }
8104
8105 int map_element_RND_to_SP(int element_rnd)
8106 {
8107   int element_sp = 0x20;        // map unknown elements to yellow "hardware"
8108
8109   if (element_rnd >= EL_SP_START &&
8110       element_rnd <= EL_SP_END)
8111     element_sp = element_rnd - EL_SP_START;
8112   else if (element_rnd == EL_EMPTY_SPACE)
8113     element_sp = 0x00;
8114   else if (element_rnd == EL_INVISIBLE_WALL)
8115     element_sp = 0x28;
8116
8117   return element_sp;
8118 }
8119
8120 int map_element_SP_to_RND(int element_sp)
8121 {
8122   int element_rnd = EL_UNKNOWN;
8123
8124   if (element_sp >= 0x00 &&
8125       element_sp <= 0x27)
8126     element_rnd = EL_SP_START + element_sp;
8127   else if (element_sp == 0x28)
8128     element_rnd = EL_INVISIBLE_WALL;
8129
8130   return element_rnd;
8131 }
8132
8133 int map_action_SP_to_RND(int action_sp)
8134 {
8135   switch (action_sp)
8136   {
8137     case actActive:             return ACTION_ACTIVE;
8138     case actImpact:             return ACTION_IMPACT;
8139     case actExploding:          return ACTION_EXPLODING;
8140     case actDigging:            return ACTION_DIGGING;
8141     case actSnapping:           return ACTION_SNAPPING;
8142     case actCollecting:         return ACTION_COLLECTING;
8143     case actPassing:            return ACTION_PASSING;
8144     case actPushing:            return ACTION_PUSHING;
8145     case actDropping:           return ACTION_DROPPING;
8146
8147     default:                    return ACTION_DEFAULT;
8148   }
8149 }
8150
8151 int map_element_RND_to_MM(int element_rnd)
8152 {
8153   return (element_rnd >= EL_MM_START_1 &&
8154           element_rnd <= EL_MM_END_1 ?
8155           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8156
8157           element_rnd >= EL_MM_START_2 &&
8158           element_rnd <= EL_MM_END_2 ?
8159           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8160
8161           element_rnd >= EL_MM_START_3 &&
8162           element_rnd <= EL_MM_END_3 ?
8163           EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8164
8165           element_rnd >= EL_CHAR_START &&
8166           element_rnd <= EL_CHAR_END ?
8167           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8168
8169           element_rnd >= EL_MM_RUNTIME_START &&
8170           element_rnd <= EL_MM_RUNTIME_END ?
8171           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8172
8173           EL_MM_EMPTY_NATIVE);
8174 }
8175
8176 int map_element_MM_to_RND(int element_mm)
8177 {
8178   return (element_mm == EL_MM_EMPTY_NATIVE ||
8179           element_mm == EL_DF_EMPTY_NATIVE ?
8180           EL_EMPTY :
8181
8182           element_mm >= EL_MM_START_1_NATIVE &&
8183           element_mm <= EL_MM_END_1_NATIVE ?
8184           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8185
8186           element_mm >= EL_MM_START_2_NATIVE &&
8187           element_mm <= EL_MM_END_2_NATIVE ?
8188           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8189
8190           element_mm >= EL_MM_START_3_NATIVE &&
8191           element_mm <= EL_MM_END_3_NATIVE ?
8192           EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8193
8194           element_mm >= EL_MM_CHAR_START_NATIVE &&
8195           element_mm <= EL_MM_CHAR_END_NATIVE ?
8196           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8197
8198           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8199           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8200           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8201
8202           EL_EMPTY);
8203 }
8204
8205 int map_action_MM_to_RND(int action_mm)
8206 {
8207   // all MM actions are defined to exactly match their RND counterparts
8208   return action_mm;
8209 }
8210
8211 int map_sound_MM_to_RND(int sound_mm)
8212 {
8213   switch (sound_mm)
8214   {
8215     case SND_MM_GAME_LEVELTIME_CHARGING:
8216       return SND_GAME_LEVELTIME_CHARGING;
8217
8218     case SND_MM_GAME_HEALTH_CHARGING:
8219       return SND_GAME_HEALTH_CHARGING;
8220
8221     default:
8222       return SND_UNDEFINED;
8223   }
8224 }
8225
8226 int map_mm_wall_element(int element)
8227 {
8228   return (element >= EL_MM_STEEL_WALL_START &&
8229           element <= EL_MM_STEEL_WALL_END ?
8230           EL_MM_STEEL_WALL :
8231
8232           element >= EL_MM_WOODEN_WALL_START &&
8233           element <= EL_MM_WOODEN_WALL_END ?
8234           EL_MM_WOODEN_WALL :
8235
8236           element >= EL_MM_ICE_WALL_START &&
8237           element <= EL_MM_ICE_WALL_END ?
8238           EL_MM_ICE_WALL :
8239
8240           element >= EL_MM_AMOEBA_WALL_START &&
8241           element <= EL_MM_AMOEBA_WALL_END ?
8242           EL_MM_AMOEBA_WALL :
8243
8244           element >= EL_DF_STEEL_WALL_START &&
8245           element <= EL_DF_STEEL_WALL_END ?
8246           EL_DF_STEEL_WALL :
8247
8248           element >= EL_DF_WOODEN_WALL_START &&
8249           element <= EL_DF_WOODEN_WALL_END ?
8250           EL_DF_WOODEN_WALL :
8251
8252           element);
8253 }
8254
8255 int map_mm_wall_element_editor(int element)
8256 {
8257   switch (element)
8258   {
8259     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
8260     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
8261     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
8262     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
8263     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
8264     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
8265
8266     default:                    return element;
8267   }
8268 }
8269
8270 int get_next_element(int element)
8271 {
8272   switch (element)
8273   {
8274     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
8275     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
8276     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
8277     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
8278     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
8279     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
8280     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
8281     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
8282     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
8283     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
8284     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
8285
8286     default:                            return element;
8287   }
8288 }
8289
8290 int el2img_mm(int element_mm)
8291 {
8292   return el2img(map_element_MM_to_RND(element_mm));
8293 }
8294
8295 int el_act2img_mm(int element_mm, int action)
8296 {
8297   return el_act2img(map_element_MM_to_RND(element_mm), action);
8298 }
8299
8300 int el_act_dir2img(int element, int action, int direction)
8301 {
8302   element = GFX_ELEMENT(element);
8303   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8304
8305   // direction_graphic[][] == graphic[] for undefined direction graphics
8306   return element_info[element].direction_graphic[action][direction];
8307 }
8308
8309 static int el_act_dir2crm(int element, int action, int direction)
8310 {
8311   element = GFX_ELEMENT(element);
8312   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8313
8314   // direction_graphic[][] == graphic[] for undefined direction graphics
8315   return element_info[element].direction_crumbled[action][direction];
8316 }
8317
8318 int el_act2img(int element, int action)
8319 {
8320   element = GFX_ELEMENT(element);
8321
8322   return element_info[element].graphic[action];
8323 }
8324
8325 int el_act2crm(int element, int action)
8326 {
8327   element = GFX_ELEMENT(element);
8328
8329   return element_info[element].crumbled[action];
8330 }
8331
8332 int el_dir2img(int element, int direction)
8333 {
8334   element = GFX_ELEMENT(element);
8335
8336   return el_act_dir2img(element, ACTION_DEFAULT, direction);
8337 }
8338
8339 int el2baseimg(int element)
8340 {
8341   return element_info[element].graphic[ACTION_DEFAULT];
8342 }
8343
8344 int el2img(int element)
8345 {
8346   element = GFX_ELEMENT(element);
8347
8348   return element_info[element].graphic[ACTION_DEFAULT];
8349 }
8350
8351 int el2edimg(int element)
8352 {
8353   element = GFX_ELEMENT(element);
8354
8355   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8356 }
8357
8358 int el2preimg(int element)
8359 {
8360   element = GFX_ELEMENT(element);
8361
8362   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8363 }
8364
8365 int el2panelimg(int element)
8366 {
8367   element = GFX_ELEMENT(element);
8368
8369   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8370 }
8371
8372 int font2baseimg(int font_nr)
8373 {
8374   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8375 }
8376
8377 int getBeltNrFromBeltElement(int element)
8378 {
8379   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8380           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8381           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8382 }
8383
8384 int getBeltNrFromBeltActiveElement(int element)
8385 {
8386   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8387           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8388           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8389 }
8390
8391 int getBeltNrFromBeltSwitchElement(int element)
8392 {
8393   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8394           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8395           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8396 }
8397
8398 int getBeltDirNrFromBeltElement(int element)
8399 {
8400   static int belt_base_element[4] =
8401   {
8402     EL_CONVEYOR_BELT_1_LEFT,
8403     EL_CONVEYOR_BELT_2_LEFT,
8404     EL_CONVEYOR_BELT_3_LEFT,
8405     EL_CONVEYOR_BELT_4_LEFT
8406   };
8407
8408   int belt_nr = getBeltNrFromBeltElement(element);
8409   int belt_dir_nr = element - belt_base_element[belt_nr];
8410
8411   return (belt_dir_nr % 3);
8412 }
8413
8414 int getBeltDirNrFromBeltSwitchElement(int element)
8415 {
8416   static int belt_base_element[4] =
8417   {
8418     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8419     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8420     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8421     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8422   };
8423
8424   int belt_nr = getBeltNrFromBeltSwitchElement(element);
8425   int belt_dir_nr = element - belt_base_element[belt_nr];
8426
8427   return (belt_dir_nr % 3);
8428 }
8429
8430 int getBeltDirFromBeltElement(int element)
8431 {
8432   static int belt_move_dir[3] =
8433   {
8434     MV_LEFT,
8435     MV_NONE,
8436     MV_RIGHT
8437   };
8438
8439   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8440
8441   return belt_move_dir[belt_dir_nr];
8442 }
8443
8444 int getBeltDirFromBeltSwitchElement(int element)
8445 {
8446   static int belt_move_dir[3] =
8447   {
8448     MV_LEFT,
8449     MV_NONE,
8450     MV_RIGHT
8451   };
8452
8453   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8454
8455   return belt_move_dir[belt_dir_nr];
8456 }
8457
8458 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8459 {
8460   static int belt_base_element[4] =
8461   {
8462     EL_CONVEYOR_BELT_1_LEFT,
8463     EL_CONVEYOR_BELT_2_LEFT,
8464     EL_CONVEYOR_BELT_3_LEFT,
8465     EL_CONVEYOR_BELT_4_LEFT
8466   };
8467
8468   return belt_base_element[belt_nr] + belt_dir_nr;
8469 }
8470
8471 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8472 {
8473   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8474
8475   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8476 }
8477
8478 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8479 {
8480   static int belt_base_element[4] =
8481   {
8482     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8483     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8484     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8485     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8486   };
8487
8488   return belt_base_element[belt_nr] + belt_dir_nr;
8489 }
8490
8491 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8492 {
8493   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8494
8495   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8496 }
8497
8498 boolean swapTiles_EM(boolean is_pre_emc_cave)
8499 {
8500   return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8501 }
8502
8503 boolean getTeamMode_EM(void)
8504 {
8505   return game.team_mode || network_playing;
8506 }
8507
8508 boolean isActivePlayer_EM(int player_nr)
8509 {
8510   return stored_player[player_nr].active;
8511 }
8512
8513 unsigned int InitRND(int seed)
8514 {
8515   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8516     return InitEngineRandom_EM(seed);
8517   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8518     return InitEngineRandom_SP(seed);
8519   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8520     return InitEngineRandom_MM(seed);
8521   else
8522     return InitEngineRandom_RND(seed);
8523 }
8524
8525 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8526 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8527
8528 static int get_effective_element_EM(int tile, int frame_em)
8529 {
8530   int element             = object_mapping[tile].element_rnd;
8531   int action              = object_mapping[tile].action;
8532   boolean is_backside     = object_mapping[tile].is_backside;
8533   boolean action_removing = (action == ACTION_DIGGING ||
8534                              action == ACTION_SNAPPING ||
8535                              action == ACTION_COLLECTING);
8536
8537   if (frame_em < 7)
8538   {
8539     switch (tile)
8540     {
8541       case Xsplash_e:
8542       case Xsplash_w:
8543         return (frame_em > 5 ? EL_EMPTY : element);
8544
8545       default:
8546         return element;
8547     }
8548   }
8549   else  // frame_em == 7
8550   {
8551     switch (tile)
8552     {
8553       case Xsplash_e:
8554       case Xsplash_w:
8555         return EL_EMPTY;
8556
8557       case Ynut_stone:
8558         return EL_EMERALD;
8559
8560       case Ydiamond_stone:
8561         return EL_ROCK;
8562
8563       case Xdrip_stretch:
8564       case Xdrip_stretchB:
8565       case Ydrip_1_s:
8566       case Ydrip_1_sB:
8567       case Yball_1:
8568       case Xball_2:
8569       case Yball_2:
8570       case Yball_blank:
8571       case Ykey_1_blank:
8572       case Ykey_2_blank:
8573       case Ykey_3_blank:
8574       case Ykey_4_blank:
8575       case Ykey_5_blank:
8576       case Ykey_6_blank:
8577       case Ykey_7_blank:
8578       case Ykey_8_blank:
8579       case Ylenses_blank:
8580       case Ymagnify_blank:
8581       case Ygrass_blank:
8582       case Ydirt_blank:
8583       case Xsand_stonein_1:
8584       case Xsand_stonein_2:
8585       case Xsand_stonein_3:
8586       case Xsand_stonein_4:
8587         return element;
8588
8589       default:
8590         return (is_backside || action_removing ? EL_EMPTY : element);
8591     }
8592   }
8593 }
8594
8595 static boolean check_linear_animation_EM(int tile)
8596 {
8597   switch (tile)
8598   {
8599     case Xsand_stonesand_1:
8600     case Xsand_stonesand_quickout_1:
8601     case Xsand_sandstone_1:
8602     case Xsand_stonein_1:
8603     case Xsand_stoneout_1:
8604     case Xboom_1:
8605     case Xdynamite_1:
8606     case Ybug_w_n:
8607     case Ybug_n_e:
8608     case Ybug_e_s:
8609     case Ybug_s_w:
8610     case Ybug_e_n:
8611     case Ybug_s_e:
8612     case Ybug_w_s:
8613     case Ybug_n_w:
8614     case Ytank_w_n:
8615     case Ytank_n_e:
8616     case Ytank_e_s:
8617     case Ytank_s_w:
8618     case Ytank_e_n:
8619     case Ytank_s_e:
8620     case Ytank_w_s:
8621     case Ytank_n_w:
8622     case Xsplash_e:
8623     case Xsplash_w:
8624     case Ynut_stone:
8625       return TRUE;
8626   }
8627
8628   return FALSE;
8629 }
8630
8631 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8632                                      boolean has_crumbled_graphics,
8633                                      int crumbled, int sync_frame)
8634 {
8635   // if element can be crumbled, but certain action graphics are just empty
8636   // space (like instantly snapping sand to empty space in 1 frame), do not
8637   // treat these empty space graphics as crumbled graphics in EMC engine
8638   if (crumbled == IMG_EMPTY_SPACE)
8639     has_crumbled_graphics = FALSE;
8640
8641   if (has_crumbled_graphics)
8642   {
8643     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8644     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8645                                            g_crumbled->anim_delay,
8646                                            g_crumbled->anim_mode,
8647                                            g_crumbled->anim_start_frame,
8648                                            sync_frame);
8649
8650     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8651                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8652
8653     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8654     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8655
8656     g_em->has_crumbled_graphics = TRUE;
8657   }
8658   else
8659   {
8660     g_em->crumbled_bitmap = NULL;
8661     g_em->crumbled_src_x = 0;
8662     g_em->crumbled_src_y = 0;
8663     g_em->crumbled_border_size = 0;
8664     g_em->crumbled_tile_size = 0;
8665
8666     g_em->has_crumbled_graphics = FALSE;
8667   }
8668 }
8669
8670 #if 0
8671 void ResetGfxAnimation_EM(int x, int y, int tile)
8672 {
8673   GfxFrame[x][y] = 0;
8674 }
8675 #endif
8676
8677 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8678                         int tile, int frame_em, int x, int y)
8679 {
8680   int action = object_mapping[tile].action;
8681   int direction = object_mapping[tile].direction;
8682   int effective_element = get_effective_element_EM(tile, frame_em);
8683   int graphic = (direction == MV_NONE ?
8684                  el_act2img(effective_element, action) :
8685                  el_act_dir2img(effective_element, action, direction));
8686   struct GraphicInfo *g = &graphic_info[graphic];
8687   int sync_frame;
8688   boolean action_removing = (action == ACTION_DIGGING ||
8689                              action == ACTION_SNAPPING ||
8690                              action == ACTION_COLLECTING);
8691   boolean action_moving   = (action == ACTION_FALLING ||
8692                              action == ACTION_MOVING ||
8693                              action == ACTION_PUSHING ||
8694                              action == ACTION_EATING ||
8695                              action == ACTION_FILLING ||
8696                              action == ACTION_EMPTYING);
8697   boolean action_falling  = (action == ACTION_FALLING ||
8698                              action == ACTION_FILLING ||
8699                              action == ACTION_EMPTYING);
8700
8701   // special case: graphic uses "2nd movement tile" and has defined
8702   // 7 frames for movement animation (or less) => use default graphic
8703   // for last (8th) frame which ends the movement animation
8704   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8705   {
8706     action = ACTION_DEFAULT;    // (keep action_* unchanged for now)
8707     graphic = (direction == MV_NONE ?
8708                el_act2img(effective_element, action) :
8709                el_act_dir2img(effective_element, action, direction));
8710
8711     g = &graphic_info[graphic];
8712   }
8713
8714   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8715   {
8716     GfxFrame[x][y] = 0;
8717   }
8718   else if (action_moving)
8719   {
8720     boolean is_backside = object_mapping[tile].is_backside;
8721
8722     if (is_backside)
8723     {
8724       int direction = object_mapping[tile].direction;
8725       int move_dir = (action_falling ? MV_DOWN : direction);
8726
8727       GfxFrame[x][y]++;
8728
8729 #if 1
8730       // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8731       if (g->double_movement && frame_em == 0)
8732         GfxFrame[x][y] = 0;
8733 #endif
8734
8735       if (move_dir == MV_LEFT)
8736         GfxFrame[x - 1][y] = GfxFrame[x][y];
8737       else if (move_dir == MV_RIGHT)
8738         GfxFrame[x + 1][y] = GfxFrame[x][y];
8739       else if (move_dir == MV_UP)
8740         GfxFrame[x][y - 1] = GfxFrame[x][y];
8741       else if (move_dir == MV_DOWN)
8742         GfxFrame[x][y + 1] = GfxFrame[x][y];
8743     }
8744   }
8745   else
8746   {
8747     GfxFrame[x][y]++;
8748
8749     // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8750     if (tile == Xsand_stonesand_quickout_1 ||
8751         tile == Xsand_stonesand_quickout_2)
8752       GfxFrame[x][y]++;
8753   }
8754
8755   if (graphic_info[graphic].anim_global_sync)
8756     sync_frame = FrameCounter;
8757   else if (graphic_info[graphic].anim_global_anim_sync)
8758     sync_frame = getGlobalAnimSyncFrame();
8759   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8760     sync_frame = GfxFrame[x][y];
8761   else
8762     sync_frame = 0;     // playfield border (pseudo steel)
8763
8764   SetRandomAnimationValue(x, y);
8765
8766   int frame = getAnimationFrame(g->anim_frames,
8767                                 g->anim_delay,
8768                                 g->anim_mode,
8769                                 g->anim_start_frame,
8770                                 sync_frame);
8771
8772   g_em->unique_identifier =
8773     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8774 }
8775
8776 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8777                                   int tile, int frame_em, int x, int y)
8778 {
8779   int action = object_mapping[tile].action;
8780   int direction = object_mapping[tile].direction;
8781   boolean is_backside = object_mapping[tile].is_backside;
8782   int effective_element = get_effective_element_EM(tile, frame_em);
8783   int effective_action = action;
8784   int graphic = (direction == MV_NONE ?
8785                  el_act2img(effective_element, effective_action) :
8786                  el_act_dir2img(effective_element, effective_action,
8787                                 direction));
8788   int crumbled = (direction == MV_NONE ?
8789                   el_act2crm(effective_element, effective_action) :
8790                   el_act_dir2crm(effective_element, effective_action,
8791                                  direction));
8792   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8793   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8794   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8795   struct GraphicInfo *g = &graphic_info[graphic];
8796   int sync_frame;
8797
8798   // special case: graphic uses "2nd movement tile" and has defined
8799   // 7 frames for movement animation (or less) => use default graphic
8800   // for last (8th) frame which ends the movement animation
8801   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8802   {
8803     effective_action = ACTION_DEFAULT;
8804     graphic = (direction == MV_NONE ?
8805                el_act2img(effective_element, effective_action) :
8806                el_act_dir2img(effective_element, effective_action,
8807                               direction));
8808     crumbled = (direction == MV_NONE ?
8809                 el_act2crm(effective_element, effective_action) :
8810                 el_act_dir2crm(effective_element, effective_action,
8811                                direction));
8812
8813     g = &graphic_info[graphic];
8814   }
8815
8816   if (graphic_info[graphic].anim_global_sync)
8817     sync_frame = FrameCounter;
8818   else if (graphic_info[graphic].anim_global_anim_sync)
8819     sync_frame = getGlobalAnimSyncFrame();
8820   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8821     sync_frame = GfxFrame[x][y];
8822   else
8823     sync_frame = 0;     // playfield border (pseudo steel)
8824
8825   SetRandomAnimationValue(x, y);
8826
8827   int frame = getAnimationFrame(g->anim_frames,
8828                                 g->anim_delay,
8829                                 g->anim_mode,
8830                                 g->anim_start_frame,
8831                                 sync_frame);
8832
8833   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8834                       g->double_movement && is_backside);
8835
8836   // (updating the "crumbled" graphic definitions is probably not really needed,
8837   // as animations for crumbled graphics can't be longer than one EMC cycle)
8838   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8839                            sync_frame);
8840 }
8841
8842 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8843                                   int player_nr, int anim, int frame_em)
8844 {
8845   int element   = player_mapping[player_nr][anim].element_rnd;
8846   int action    = player_mapping[player_nr][anim].action;
8847   int direction = player_mapping[player_nr][anim].direction;
8848   int graphic = (direction == MV_NONE ?
8849                  el_act2img(element, action) :
8850                  el_act_dir2img(element, action, direction));
8851   struct GraphicInfo *g = &graphic_info[graphic];
8852   int sync_frame;
8853
8854   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8855
8856   stored_player[player_nr].StepFrame = frame_em;
8857
8858   sync_frame = stored_player[player_nr].Frame;
8859
8860   int frame = getAnimationFrame(g->anim_frames,
8861                                 g->anim_delay,
8862                                 g->anim_mode,
8863                                 g->anim_start_frame,
8864                                 sync_frame);
8865
8866   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8867                       &g_em->src_x, &g_em->src_y, FALSE);
8868 }
8869
8870 void InitGraphicInfo_EM(void)
8871 {
8872   int i, j, p;
8873
8874   // always start with reliable default values
8875   for (i = 0; i < GAME_TILE_MAX; i++)
8876   {
8877     object_mapping[i].element_rnd = EL_UNKNOWN;
8878     object_mapping[i].is_backside = FALSE;
8879     object_mapping[i].action = ACTION_DEFAULT;
8880     object_mapping[i].direction = MV_NONE;
8881   }
8882
8883   // always start with reliable default values
8884   for (p = 0; p < MAX_PLAYERS; p++)
8885   {
8886     for (i = 0; i < PLY_MAX; i++)
8887     {
8888       player_mapping[p][i].element_rnd = EL_UNKNOWN;
8889       player_mapping[p][i].action = ACTION_DEFAULT;
8890       player_mapping[p][i].direction = MV_NONE;
8891     }
8892   }
8893
8894   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8895   {
8896     int e = em_object_mapping_list[i].element_em;
8897
8898     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8899     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8900
8901     if (em_object_mapping_list[i].action != -1)
8902       object_mapping[e].action = em_object_mapping_list[i].action;
8903
8904     if (em_object_mapping_list[i].direction != -1)
8905       object_mapping[e].direction =
8906         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8907   }
8908
8909   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8910   {
8911     int a = em_player_mapping_list[i].action_em;
8912     int p = em_player_mapping_list[i].player_nr;
8913
8914     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8915
8916     if (em_player_mapping_list[i].action != -1)
8917       player_mapping[p][a].action = em_player_mapping_list[i].action;
8918
8919     if (em_player_mapping_list[i].direction != -1)
8920       player_mapping[p][a].direction =
8921         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8922   }
8923
8924   for (i = 0; i < GAME_TILE_MAX; i++)
8925   {
8926     int element = object_mapping[i].element_rnd;
8927     int action = object_mapping[i].action;
8928     int direction = object_mapping[i].direction;
8929     boolean is_backside = object_mapping[i].is_backside;
8930     boolean action_exploding = ((action == ACTION_EXPLODING ||
8931                                  action == ACTION_SMASHED_BY_ROCK ||
8932                                  action == ACTION_SMASHED_BY_SPRING) &&
8933                                 element != EL_DIAMOND);
8934     boolean action_active = (action == ACTION_ACTIVE);
8935     boolean action_other = (action == ACTION_OTHER);
8936
8937     for (j = 0; j < 8; j++)
8938     {
8939       int effective_element = get_effective_element_EM(i, j);
8940       int effective_action = (j < 7 ? action :
8941                               i == Xdrip_stretch ? action :
8942                               i == Xdrip_stretchB ? action :
8943                               i == Ydrip_1_s ? action :
8944                               i == Ydrip_1_sB ? action :
8945                               i == Yball_1 ? action :
8946                               i == Xball_2 ? action :
8947                               i == Yball_2 ? action :
8948                               i == Yball_blank ? action :
8949                               i == Ykey_1_blank ? action :
8950                               i == Ykey_2_blank ? action :
8951                               i == Ykey_3_blank ? action :
8952                               i == Ykey_4_blank ? action :
8953                               i == Ykey_5_blank ? action :
8954                               i == Ykey_6_blank ? action :
8955                               i == Ykey_7_blank ? action :
8956                               i == Ykey_8_blank ? action :
8957                               i == Ylenses_blank ? action :
8958                               i == Ymagnify_blank ? action :
8959                               i == Ygrass_blank ? action :
8960                               i == Ydirt_blank ? action :
8961                               i == Xsand_stonein_1 ? action :
8962                               i == Xsand_stonein_2 ? action :
8963                               i == Xsand_stonein_3 ? action :
8964                               i == Xsand_stonein_4 ? action :
8965                               i == Xsand_stoneout_1 ? action :
8966                               i == Xsand_stoneout_2 ? action :
8967                               i == Xboom_android ? ACTION_EXPLODING :
8968                               action_exploding ? ACTION_EXPLODING :
8969                               action_active ? action :
8970                               action_other ? action :
8971                               ACTION_DEFAULT);
8972       int graphic = (el_act_dir2img(effective_element, effective_action,
8973                                     direction));
8974       int crumbled = (el_act_dir2crm(effective_element, effective_action,
8975                                      direction));
8976       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8977       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8978       boolean has_action_graphics = (graphic != base_graphic);
8979       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8980       struct GraphicInfo *g = &graphic_info[graphic];
8981       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8982       Bitmap *src_bitmap;
8983       int src_x, src_y;
8984       // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8985       boolean special_animation = (action != ACTION_DEFAULT &&
8986                                    g->anim_frames == 3 &&
8987                                    g->anim_delay == 2 &&
8988                                    g->anim_mode & ANIM_LINEAR);
8989       int sync_frame = (i == Xdrip_stretch ? 7 :
8990                         i == Xdrip_stretchB ? 7 :
8991                         i == Ydrip_2_s ? j + 8 :
8992                         i == Ydrip_2_sB ? j + 8 :
8993                         i == Xacid_1 ? 0 :
8994                         i == Xacid_2 ? 10 :
8995                         i == Xacid_3 ? 20 :
8996                         i == Xacid_4 ? 30 :
8997                         i == Xacid_5 ? 40 :
8998                         i == Xacid_6 ? 50 :
8999                         i == Xacid_7 ? 60 :
9000                         i == Xacid_8 ? 70 :
9001                         i == Xfake_acid_1 ? 0 :
9002                         i == Xfake_acid_2 ? 10 :
9003                         i == Xfake_acid_3 ? 20 :
9004                         i == Xfake_acid_4 ? 30 :
9005                         i == Xfake_acid_5 ? 40 :
9006                         i == Xfake_acid_6 ? 50 :
9007                         i == Xfake_acid_7 ? 60 :
9008                         i == Xfake_acid_8 ? 70 :
9009                         i == Xfake_acid_1_player ? 0 :
9010                         i == Xfake_acid_2_player ? 10 :
9011                         i == Xfake_acid_3_player ? 20 :
9012                         i == Xfake_acid_4_player ? 30 :
9013                         i == Xfake_acid_5_player ? 40 :
9014                         i == Xfake_acid_6_player ? 50 :
9015                         i == Xfake_acid_7_player ? 60 :
9016                         i == Xfake_acid_8_player ? 70 :
9017                         i == Xball_2 ? 7 :
9018                         i == Yball_2 ? j + 8 :
9019                         i == Yball_blank ? j + 1 :
9020                         i == Ykey_1_blank ? j + 1 :
9021                         i == Ykey_2_blank ? j + 1 :
9022                         i == Ykey_3_blank ? j + 1 :
9023                         i == Ykey_4_blank ? j + 1 :
9024                         i == Ykey_5_blank ? j + 1 :
9025                         i == Ykey_6_blank ? j + 1 :
9026                         i == Ykey_7_blank ? j + 1 :
9027                         i == Ykey_8_blank ? j + 1 :
9028                         i == Ylenses_blank ? j + 1 :
9029                         i == Ymagnify_blank ? j + 1 :
9030                         i == Ygrass_blank ? j + 1 :
9031                         i == Ydirt_blank ? j + 1 :
9032                         i == Xamoeba_1 ? 0 :
9033                         i == Xamoeba_2 ? 1 :
9034                         i == Xamoeba_3 ? 2 :
9035                         i == Xamoeba_4 ? 3 :
9036                         i == Xamoeba_5 ? 0 :
9037                         i == Xamoeba_6 ? 1 :
9038                         i == Xamoeba_7 ? 2 :
9039                         i == Xamoeba_8 ? 3 :
9040                         i == Xexit_2 ? j + 8 :
9041                         i == Xexit_3 ? j + 16 :
9042                         i == Xdynamite_1 ? 0 :
9043                         i == Xdynamite_2 ? 8 :
9044                         i == Xdynamite_3 ? 16 :
9045                         i == Xdynamite_4 ? 24 :
9046                         i == Xsand_stonein_1 ? j + 1 :
9047                         i == Xsand_stonein_2 ? j + 9 :
9048                         i == Xsand_stonein_3 ? j + 17 :
9049                         i == Xsand_stonein_4 ? j + 25 :
9050                         i == Xsand_stoneout_1 && j == 0 ? 0 :
9051                         i == Xsand_stoneout_1 && j == 1 ? 0 :
9052                         i == Xsand_stoneout_1 && j == 2 ? 1 :
9053                         i == Xsand_stoneout_1 && j == 3 ? 2 :
9054                         i == Xsand_stoneout_1 && j == 4 ? 2 :
9055                         i == Xsand_stoneout_1 && j == 5 ? 3 :
9056                         i == Xsand_stoneout_1 && j == 6 ? 4 :
9057                         i == Xsand_stoneout_1 && j == 7 ? 4 :
9058                         i == Xsand_stoneout_2 && j == 0 ? 5 :
9059                         i == Xsand_stoneout_2 && j == 1 ? 6 :
9060                         i == Xsand_stoneout_2 && j == 2 ? 7 :
9061                         i == Xsand_stoneout_2 && j == 3 ? 8 :
9062                         i == Xsand_stoneout_2 && j == 4 ? 9 :
9063                         i == Xsand_stoneout_2 && j == 5 ? 11 :
9064                         i == Xsand_stoneout_2 && j == 6 ? 13 :
9065                         i == Xsand_stoneout_2 && j == 7 ? 15 :
9066                         i == Xboom_bug && j == 1 ? 2 :
9067                         i == Xboom_bug && j == 2 ? 2 :
9068                         i == Xboom_bug && j == 3 ? 4 :
9069                         i == Xboom_bug && j == 4 ? 4 :
9070                         i == Xboom_bug && j == 5 ? 2 :
9071                         i == Xboom_bug && j == 6 ? 2 :
9072                         i == Xboom_bug && j == 7 ? 0 :
9073                         i == Xboom_tank && j == 1 ? 2 :
9074                         i == Xboom_tank && j == 2 ? 2 :
9075                         i == Xboom_tank && j == 3 ? 4 :
9076                         i == Xboom_tank && j == 4 ? 4 :
9077                         i == Xboom_tank && j == 5 ? 2 :
9078                         i == Xboom_tank && j == 6 ? 2 :
9079                         i == Xboom_tank && j == 7 ? 0 :
9080                         i == Xboom_android && j == 7 ? 6 :
9081                         i == Xboom_1 && j == 1 ? 2 :
9082                         i == Xboom_1 && j == 2 ? 2 :
9083                         i == Xboom_1 && j == 3 ? 4 :
9084                         i == Xboom_1 && j == 4 ? 4 :
9085                         i == Xboom_1 && j == 5 ? 6 :
9086                         i == Xboom_1 && j == 6 ? 6 :
9087                         i == Xboom_1 && j == 7 ? 8 :
9088                         i == Xboom_2 && j == 0 ? 8 :
9089                         i == Xboom_2 && j == 1 ? 8 :
9090                         i == Xboom_2 && j == 2 ? 10 :
9091                         i == Xboom_2 && j == 3 ? 10 :
9092                         i == Xboom_2 && j == 4 ? 10 :
9093                         i == Xboom_2 && j == 5 ? 12 :
9094                         i == Xboom_2 && j == 6 ? 12 :
9095                         i == Xboom_2 && j == 7 ? 12 :
9096                         special_animation && j == 4 ? 3 :
9097                         effective_action != action ? 0 :
9098                         j);
9099       int frame = getAnimationFrame(g->anim_frames,
9100                                     g->anim_delay,
9101                                     g->anim_mode,
9102                                     g->anim_start_frame,
9103                                     sync_frame);
9104
9105       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9106                           g->double_movement && is_backside);
9107
9108       g_em->bitmap = src_bitmap;
9109       g_em->src_x = src_x;
9110       g_em->src_y = src_y;
9111       g_em->src_offset_x = 0;
9112       g_em->src_offset_y = 0;
9113       g_em->dst_offset_x = 0;
9114       g_em->dst_offset_y = 0;
9115       g_em->width  = TILEX;
9116       g_em->height = TILEY;
9117
9118       g_em->preserve_background = FALSE;
9119
9120       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9121                                sync_frame);
9122
9123       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9124                                    effective_action == ACTION_MOVING  ||
9125                                    effective_action == ACTION_PUSHING ||
9126                                    effective_action == ACTION_EATING)) ||
9127           (!has_action_graphics && (effective_action == ACTION_FILLING ||
9128                                     effective_action == ACTION_EMPTYING)))
9129       {
9130         int move_dir =
9131           (effective_action == ACTION_FALLING ||
9132            effective_action == ACTION_FILLING ||
9133            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9134         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9135         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
9136         int num_steps = (i == Ydrip_1_s  ? 16 :
9137                          i == Ydrip_1_sB ? 16 :
9138                          i == Ydrip_2_s  ? 16 :
9139                          i == Ydrip_2_sB ? 16 :
9140                          i == Xsand_stonein_1 ? 32 :
9141                          i == Xsand_stonein_2 ? 32 :
9142                          i == Xsand_stonein_3 ? 32 :
9143                          i == Xsand_stonein_4 ? 32 :
9144                          i == Xsand_stoneout_1 ? 16 :
9145                          i == Xsand_stoneout_2 ? 16 : 8);
9146         int cx = ABS(dx) * (TILEX / num_steps);
9147         int cy = ABS(dy) * (TILEY / num_steps);
9148         int step_frame = (i == Ydrip_2_s        ? j + 8 :
9149                           i == Ydrip_2_sB       ? j + 8 :
9150                           i == Xsand_stonein_2  ? j + 8 :
9151                           i == Xsand_stonein_3  ? j + 16 :
9152                           i == Xsand_stonein_4  ? j + 24 :
9153                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9154         int step = (is_backside ? step_frame : num_steps - step_frame);
9155
9156         if (is_backside)        // tile where movement starts
9157         {
9158           if (dx < 0 || dy < 0)
9159           {
9160             g_em->src_offset_x = cx * step;
9161             g_em->src_offset_y = cy * step;
9162           }
9163           else
9164           {
9165             g_em->dst_offset_x = cx * step;
9166             g_em->dst_offset_y = cy * step;
9167           }
9168         }
9169         else                    // tile where movement ends
9170         {
9171           if (dx < 0 || dy < 0)
9172           {
9173             g_em->dst_offset_x = cx * step;
9174             g_em->dst_offset_y = cy * step;
9175           }
9176           else
9177           {
9178             g_em->src_offset_x = cx * step;
9179             g_em->src_offset_y = cy * step;
9180           }
9181         }
9182
9183         g_em->width  = TILEX - cx * step;
9184         g_em->height = TILEY - cy * step;
9185       }
9186
9187       // create unique graphic identifier to decide if tile must be redrawn
9188       /* bit 31 - 16 (16 bit): EM style graphic
9189          bit 15 - 12 ( 4 bit): EM style frame
9190          bit 11 -  6 ( 6 bit): graphic width
9191          bit  5 -  0 ( 6 bit): graphic height */
9192       g_em->unique_identifier =
9193         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9194     }
9195   }
9196
9197   for (i = 0; i < GAME_TILE_MAX; i++)
9198   {
9199     for (j = 0; j < 8; j++)
9200     {
9201       int element = object_mapping[i].element_rnd;
9202       int action = object_mapping[i].action;
9203       int direction = object_mapping[i].direction;
9204       boolean is_backside = object_mapping[i].is_backside;
9205       int graphic_action  = el_act_dir2img(element, action, direction);
9206       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9207
9208       if ((action == ACTION_SMASHED_BY_ROCK ||
9209            action == ACTION_SMASHED_BY_SPRING ||
9210            action == ACTION_EATING) &&
9211           graphic_action == graphic_default)
9212       {
9213         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
9214                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9215                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
9216                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9217                  Xspring);
9218
9219         // no separate animation for "smashed by rock" -- use rock instead
9220         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9221         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9222
9223         g_em->bitmap            = g_xx->bitmap;
9224         g_em->src_x             = g_xx->src_x;
9225         g_em->src_y             = g_xx->src_y;
9226         g_em->src_offset_x      = g_xx->src_offset_x;
9227         g_em->src_offset_y      = g_xx->src_offset_y;
9228         g_em->dst_offset_x      = g_xx->dst_offset_x;
9229         g_em->dst_offset_y      = g_xx->dst_offset_y;
9230         g_em->width             = g_xx->width;
9231         g_em->height            = g_xx->height;
9232         g_em->unique_identifier = g_xx->unique_identifier;
9233
9234         if (!is_backside)
9235           g_em->preserve_background = TRUE;
9236       }
9237     }
9238   }
9239
9240   for (p = 0; p < MAX_PLAYERS; p++)
9241   {
9242     for (i = 0; i < PLY_MAX; i++)
9243     {
9244       int element = player_mapping[p][i].element_rnd;
9245       int action = player_mapping[p][i].action;
9246       int direction = player_mapping[p][i].direction;
9247
9248       for (j = 0; j < 8; j++)
9249       {
9250         int effective_element = element;
9251         int effective_action = action;
9252         int graphic = (direction == MV_NONE ?
9253                        el_act2img(effective_element, effective_action) :
9254                        el_act_dir2img(effective_element, effective_action,
9255                                       direction));
9256         struct GraphicInfo *g = &graphic_info[graphic];
9257         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9258         Bitmap *src_bitmap;
9259         int src_x, src_y;
9260         int sync_frame = j;
9261         int frame = getAnimationFrame(g->anim_frames,
9262                                       g->anim_delay,
9263                                       g->anim_mode,
9264                                       g->anim_start_frame,
9265                                       sync_frame);
9266
9267         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9268
9269         g_em->bitmap = src_bitmap;
9270         g_em->src_x = src_x;
9271         g_em->src_y = src_y;
9272         g_em->src_offset_x = 0;
9273         g_em->src_offset_y = 0;
9274         g_em->dst_offset_x = 0;
9275         g_em->dst_offset_y = 0;
9276         g_em->width  = TILEX;
9277         g_em->height = TILEY;
9278       }
9279     }
9280   }
9281 }
9282
9283 static void CheckSaveEngineSnapshot_EM(int frame,
9284                                        boolean any_player_moving,
9285                                        boolean any_player_snapping,
9286                                        boolean any_player_dropping)
9287 {
9288   if (frame == 7 && !any_player_dropping)
9289   {
9290     if (!local_player->was_waiting)
9291     {
9292       if (!CheckSaveEngineSnapshotToList())
9293         return;
9294
9295       local_player->was_waiting = TRUE;
9296     }
9297   }
9298   else if (any_player_moving || any_player_snapping || any_player_dropping)
9299   {
9300     local_player->was_waiting = FALSE;
9301   }
9302 }
9303
9304 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9305                                        boolean murphy_is_dropping)
9306 {
9307   if (murphy_is_waiting)
9308   {
9309     if (!local_player->was_waiting)
9310     {
9311       if (!CheckSaveEngineSnapshotToList())
9312         return;
9313
9314       local_player->was_waiting = TRUE;
9315     }
9316   }
9317   else
9318   {
9319     local_player->was_waiting = FALSE;
9320   }
9321 }
9322
9323 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9324                                        boolean button_released)
9325 {
9326   if (button_released)
9327   {
9328     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9329       CheckSaveEngineSnapshotToList();
9330   }
9331   else if (element_clicked)
9332   {
9333     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9334       CheckSaveEngineSnapshotToList();
9335
9336     game.snapshot.changed_action = TRUE;
9337   }
9338 }
9339
9340 boolean CheckSingleStepMode_EM(int frame,
9341                                boolean any_player_moving,
9342                                boolean any_player_snapping,
9343                                boolean any_player_dropping)
9344 {
9345   if (tape.single_step && tape.recording && !tape.pausing)
9346     if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9347       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9348
9349   CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9350                              any_player_snapping, any_player_dropping);
9351
9352   return tape.pausing;
9353 }
9354
9355 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9356                             boolean murphy_is_dropping)
9357 {
9358   boolean murphy_starts_dropping = FALSE;
9359   int i;
9360
9361   for (i = 0; i < MAX_PLAYERS; i++)
9362     if (stored_player[i].force_dropping)
9363       murphy_starts_dropping = TRUE;
9364
9365   if (tape.single_step && tape.recording && !tape.pausing)
9366     if (murphy_is_waiting && !murphy_starts_dropping)
9367       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9368
9369   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9370 }
9371
9372 void CheckSingleStepMode_MM(boolean element_clicked,
9373                             boolean button_released)
9374 {
9375   if (tape.single_step && tape.recording && !tape.pausing)
9376     if (button_released)
9377       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9378
9379   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9380 }
9381
9382 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9383                          int graphic, int sync_frame)
9384 {
9385   int frame = getGraphicAnimationFrame(graphic, sync_frame);
9386
9387   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9388 }
9389
9390 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9391 {
9392   return (IS_NEXT_FRAME(sync_frame, graphic));
9393 }
9394
9395 int getGraphicInfo_Delay(int graphic)
9396 {
9397   return graphic_info[graphic].anim_delay;
9398 }
9399
9400 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9401 {
9402   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9403     return FALSE;
9404
9405   if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9406     return FALSE;
9407
9408   return TRUE;
9409 }
9410
9411 void PlayMenuSoundExt(int sound)
9412 {
9413   if (sound == SND_UNDEFINED)
9414     return;
9415
9416   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9417       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9418     return;
9419
9420   if (IS_LOOP_SOUND(sound))
9421     PlaySoundLoop(sound);
9422   else
9423     PlaySound(sound);
9424 }
9425
9426 void PlayMenuSound(void)
9427 {
9428   PlayMenuSoundExt(menu.sound[game_status]);
9429 }
9430
9431 void PlayMenuSoundStereo(int sound, int stereo_position)
9432 {
9433   if (sound == SND_UNDEFINED)
9434     return;
9435
9436   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9437       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9438     return;
9439
9440   if (IS_LOOP_SOUND(sound))
9441     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9442   else
9443     PlaySoundStereo(sound, stereo_position);
9444 }
9445
9446 void PlayMenuSoundIfLoopExt(int sound)
9447 {
9448   if (sound == SND_UNDEFINED)
9449     return;
9450
9451   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9452       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9453     return;
9454
9455   if (IS_LOOP_SOUND(sound))
9456     PlaySoundLoop(sound);
9457 }
9458
9459 void PlayMenuSoundIfLoop(void)
9460 {
9461   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9462 }
9463
9464 void PlayMenuMusicExt(int music)
9465 {
9466   if (music == MUS_UNDEFINED)
9467     return;
9468
9469   if (!setup.sound_music)
9470     return;
9471
9472   if (IS_LOOP_MUSIC(music))
9473     PlayMusicLoop(music);
9474   else
9475     PlayMusic(music);
9476 }
9477
9478 void PlayMenuMusic(void)
9479 {
9480   char *curr_music = getCurrentlyPlayingMusicFilename();
9481   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9482
9483   if (!strEqual(curr_music, next_music))
9484     PlayMenuMusicExt(menu.music[game_status]);
9485 }
9486
9487 void PlayMenuSoundsAndMusic(void)
9488 {
9489   PlayMenuSound();
9490   PlayMenuMusic();
9491 }
9492
9493 static void FadeMenuSounds(void)
9494 {
9495   FadeSounds();
9496 }
9497
9498 static void FadeMenuMusic(void)
9499 {
9500   char *curr_music = getCurrentlyPlayingMusicFilename();
9501   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9502
9503   if (!strEqual(curr_music, next_music))
9504     FadeMusic();
9505 }
9506
9507 void FadeMenuSoundsAndMusic(void)
9508 {
9509   FadeMenuSounds();
9510   FadeMenuMusic();
9511 }
9512
9513 void PlaySoundActivating(void)
9514 {
9515 #if 0
9516   PlaySound(SND_MENU_ITEM_ACTIVATING);
9517 #endif
9518 }
9519
9520 void PlaySoundSelecting(void)
9521 {
9522 #if 0
9523   PlaySound(SND_MENU_ITEM_SELECTING);
9524 #endif
9525 }
9526
9527 void ToggleFullscreenIfNeeded(void)
9528 {
9529   // if setup and video fullscreen state are already matching, nothing do do
9530   if (setup.fullscreen == video.fullscreen_enabled ||
9531       !video.fullscreen_available)
9532     return;
9533
9534   SDLSetWindowFullscreen(setup.fullscreen);
9535
9536   // set setup value according to successfully changed fullscreen mode
9537   setup.fullscreen = video.fullscreen_enabled;
9538 }
9539
9540 void ChangeWindowScalingIfNeeded(void)
9541 {
9542   // if setup and video window scaling are already matching, nothing do do
9543   if (setup.window_scaling_percent == video.window_scaling_percent ||
9544       video.fullscreen_enabled)
9545     return;
9546
9547   SDLSetWindowScaling(setup.window_scaling_percent);
9548
9549   // set setup value according to successfully changed window scaling
9550   setup.window_scaling_percent = video.window_scaling_percent;
9551 }
9552
9553 void ChangeVsyncModeIfNeeded(void)
9554 {
9555   int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9556   int video_vsync_mode = video.vsync_mode;
9557
9558   // if setup and video vsync mode are already matching, nothing do do
9559   if (setup_vsync_mode == video_vsync_mode)
9560     return;
9561
9562   // if renderer is using OpenGL, vsync mode can directly be changed
9563   SDLSetScreenVsyncMode(setup.vsync_mode);
9564
9565   // if vsync mode unchanged, try re-creating renderer to set vsync mode
9566   if (video.vsync_mode == video_vsync_mode)
9567   {
9568     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9569
9570     // save backbuffer content which gets lost when re-creating screen
9571     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9572
9573     // force re-creating screen and renderer to set new vsync mode
9574     video.fullscreen_enabled = !setup.fullscreen;
9575
9576     // when creating new renderer, destroy textures linked to old renderer
9577     FreeAllImageTextures();     // needs old renderer to free the textures
9578
9579     // re-create screen and renderer (including change of vsync mode)
9580     ChangeVideoModeIfNeeded(setup.fullscreen);
9581
9582     // set setup value according to successfully changed fullscreen mode
9583     setup.fullscreen = video.fullscreen_enabled;
9584
9585     // restore backbuffer content from temporary backbuffer backup bitmap
9586     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9587     FreeBitmap(tmp_backbuffer);
9588
9589     // update visible window/screen
9590     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9591
9592     // when changing vsync mode, re-create textures for new renderer
9593     InitImageTextures();
9594   }
9595
9596   // set setup value according to successfully changed vsync mode
9597   setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9598 }
9599
9600 static void JoinRectangles(int *x, int *y, int *width, int *height,
9601                            int x2, int y2, int width2, int height2)
9602 {
9603   // do not join with "off-screen" rectangle
9604   if (x2 == -1 || y2 == -1)
9605     return;
9606
9607   *x = MIN(*x, x2);
9608   *y = MIN(*y, y2);
9609   *width = MAX(*width, width2);
9610   *height = MAX(*height, height2);
9611 }
9612
9613 void SetAnimStatus(int anim_status_new)
9614 {
9615   if (anim_status_new == GAME_MODE_MAIN)
9616     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9617   else if (anim_status_new == GAME_MODE_NAMES)
9618     anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9619   else if (anim_status_new == GAME_MODE_SCORES)
9620     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9621
9622   global.anim_status_next = anim_status_new;
9623
9624   // directly set screen modes that are entered without fading
9625   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
9626        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9627       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
9628        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9629       (global.anim_status      == GAME_MODE_PSEUDO_NAMESONLY &&
9630        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9631       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAMES &&
9632        global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9633     global.anim_status = global.anim_status_next;
9634 }
9635
9636 void SetGameStatus(int game_status_new)
9637 {
9638   if (game_status_new != game_status)
9639     game_status_last_screen = game_status;
9640
9641   game_status = game_status_new;
9642
9643   SetAnimStatus(game_status_new);
9644 }
9645
9646 void SetFontStatus(int game_status_new)
9647 {
9648   static int last_game_status = -1;
9649
9650   if (game_status_new != -1)
9651   {
9652     // set game status for font use after storing last game status
9653     last_game_status = game_status;
9654     game_status = game_status_new;
9655   }
9656   else
9657   {
9658     // reset game status after font use from last stored game status
9659     game_status = last_game_status;
9660   }
9661 }
9662
9663 void ResetFontStatus(void)
9664 {
9665   SetFontStatus(-1);
9666 }
9667
9668 void SetLevelSetInfo(char *identifier, int level_nr)
9669 {
9670   setString(&levelset.identifier, identifier);
9671
9672   levelset.level_nr = level_nr;
9673 }
9674
9675 boolean CheckIfAllViewportsHaveChanged(void)
9676 {
9677   // if game status has not changed, viewports have not changed either
9678   if (game_status == game_status_last)
9679     return FALSE;
9680
9681   // check if all viewports have changed with current game status
9682
9683   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9684   struct RectWithBorder *vp_door_1    = &viewport.door_1[game_status];
9685   struct RectWithBorder *vp_door_2    = &viewport.door_2[game_status];
9686   int new_real_sx       = vp_playfield->x;
9687   int new_real_sy       = vp_playfield->y;
9688   int new_full_sxsize   = vp_playfield->width;
9689   int new_full_sysize   = vp_playfield->height;
9690   int new_dx            = vp_door_1->x;
9691   int new_dy            = vp_door_1->y;
9692   int new_dxsize        = vp_door_1->width;
9693   int new_dysize        = vp_door_1->height;
9694   int new_vx            = vp_door_2->x;
9695   int new_vy            = vp_door_2->y;
9696   int new_vxsize        = vp_door_2->width;
9697   int new_vysize        = vp_door_2->height;
9698
9699   boolean playfield_viewport_has_changed =
9700     (new_real_sx != REAL_SX ||
9701      new_real_sy != REAL_SY ||
9702      new_full_sxsize != FULL_SXSIZE ||
9703      new_full_sysize != FULL_SYSIZE);
9704
9705   boolean door_1_viewport_has_changed =
9706     (new_dx != DX ||
9707      new_dy != DY ||
9708      new_dxsize != DXSIZE ||
9709      new_dysize != DYSIZE);
9710
9711   boolean door_2_viewport_has_changed =
9712     (new_vx != VX ||
9713      new_vy != VY ||
9714      new_vxsize != VXSIZE ||
9715      new_vysize != VYSIZE ||
9716      game_status_last == GAME_MODE_EDITOR);
9717
9718   return (playfield_viewport_has_changed &&
9719           door_1_viewport_has_changed &&
9720           door_2_viewport_has_changed);
9721 }
9722
9723 boolean CheckFadeAll(void)
9724 {
9725   return (CheckIfGlobalBorderHasChanged() ||
9726           CheckIfAllViewportsHaveChanged());
9727 }
9728
9729 void ChangeViewportPropertiesIfNeeded(void)
9730 {
9731   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9732                                FALSE : setup.small_game_graphics);
9733   int gfx_game_mode = getGlobalGameStatus(game_status);
9734   int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9735                         gfx_game_mode);
9736   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
9737   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9738   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
9739   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
9740   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
9741   int new_win_xsize     = vp_window->width;
9742   int new_win_ysize     = vp_window->height;
9743   int border_left       = vp_playfield->border_left;
9744   int border_right      = vp_playfield->border_right;
9745   int border_top        = vp_playfield->border_top;
9746   int border_bottom     = vp_playfield->border_bottom;
9747   int new_sx            = vp_playfield->x      + border_left;
9748   int new_sy            = vp_playfield->y      + border_top;
9749   int new_sxsize        = vp_playfield->width  - border_left - border_right;
9750   int new_sysize        = vp_playfield->height - border_top  - border_bottom;
9751   int new_real_sx       = vp_playfield->x;
9752   int new_real_sy       = vp_playfield->y;
9753   int new_full_sxsize   = vp_playfield->width;
9754   int new_full_sysize   = vp_playfield->height;
9755   int new_dx            = vp_door_1->x;
9756   int new_dy            = vp_door_1->y;
9757   int new_dxsize        = vp_door_1->width;
9758   int new_dysize        = vp_door_1->height;
9759   int new_vx            = vp_door_2->x;
9760   int new_vy            = vp_door_2->y;
9761   int new_vxsize        = vp_door_2->width;
9762   int new_vysize        = vp_door_2->height;
9763   int new_ex            = vp_door_3->x;
9764   int new_ey            = vp_door_3->y;
9765   int new_exsize        = vp_door_3->width;
9766   int new_eysize        = vp_door_3->height;
9767   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9768   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9769                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9770   int new_scr_fieldx = new_sxsize / tilesize;
9771   int new_scr_fieldy = new_sysize / tilesize;
9772   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9773   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9774   boolean init_gfx_buffers = FALSE;
9775   boolean init_video_buffer = FALSE;
9776   boolean init_gadgets_and_anims = FALSE;
9777   boolean init_em_graphics = FALSE;
9778
9779   if (new_win_xsize != WIN_XSIZE ||
9780       new_win_ysize != WIN_YSIZE)
9781   {
9782     WIN_XSIZE = new_win_xsize;
9783     WIN_YSIZE = new_win_ysize;
9784
9785     init_video_buffer = TRUE;
9786     init_gfx_buffers = TRUE;
9787     init_gadgets_and_anims = TRUE;
9788
9789     // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9790   }
9791
9792   if (new_scr_fieldx != SCR_FIELDX ||
9793       new_scr_fieldy != SCR_FIELDY)
9794   {
9795     // this always toggles between MAIN and GAME when using small tile size
9796
9797     SCR_FIELDX = new_scr_fieldx;
9798     SCR_FIELDY = new_scr_fieldy;
9799
9800     // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9801   }
9802
9803   if (new_sx != SX ||
9804       new_sy != SY ||
9805       new_dx != DX ||
9806       new_dy != DY ||
9807       new_vx != VX ||
9808       new_vy != VY ||
9809       new_ex != EX ||
9810       new_ey != EY ||
9811       new_sxsize != SXSIZE ||
9812       new_sysize != SYSIZE ||
9813       new_dxsize != DXSIZE ||
9814       new_dysize != DYSIZE ||
9815       new_vxsize != VXSIZE ||
9816       new_vysize != VYSIZE ||
9817       new_exsize != EXSIZE ||
9818       new_eysize != EYSIZE ||
9819       new_real_sx != REAL_SX ||
9820       new_real_sy != REAL_SY ||
9821       new_full_sxsize != FULL_SXSIZE ||
9822       new_full_sysize != FULL_SYSIZE ||
9823       new_tilesize_var != TILESIZE_VAR
9824       )
9825   {
9826     // ------------------------------------------------------------------------
9827     // determine next fading area for changed viewport definitions
9828     // ------------------------------------------------------------------------
9829
9830     // start with current playfield area (default fading area)
9831     FADE_SX = REAL_SX;
9832     FADE_SY = REAL_SY;
9833     FADE_SXSIZE = FULL_SXSIZE;
9834     FADE_SYSIZE = FULL_SYSIZE;
9835
9836     // add new playfield area if position or size has changed
9837     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9838         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9839     {
9840       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9841                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9842     }
9843
9844     // add current and new door 1 area if position or size has changed
9845     if (new_dx != DX || new_dy != DY ||
9846         new_dxsize != DXSIZE || new_dysize != DYSIZE)
9847     {
9848       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9849                      DX, DY, DXSIZE, DYSIZE);
9850       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9851                      new_dx, new_dy, new_dxsize, new_dysize);
9852     }
9853
9854     // add current and new door 2 area if position or size has changed
9855     if (new_vx != VX || new_vy != VY ||
9856         new_vxsize != VXSIZE || new_vysize != VYSIZE)
9857     {
9858       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9859                      VX, VY, VXSIZE, VYSIZE);
9860       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9861                      new_vx, new_vy, new_vxsize, new_vysize);
9862     }
9863
9864     // ------------------------------------------------------------------------
9865     // handle changed tile size
9866     // ------------------------------------------------------------------------
9867
9868     if (new_tilesize_var != TILESIZE_VAR)
9869     {
9870       // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9871
9872       // changing tile size invalidates scroll values of engine snapshots
9873       FreeEngineSnapshotSingle();
9874
9875       // changing tile size requires update of graphic mapping for EM engine
9876       init_em_graphics = TRUE;
9877     }
9878
9879     SX = new_sx;
9880     SY = new_sy;
9881     DX = new_dx;
9882     DY = new_dy;
9883     VX = new_vx;
9884     VY = new_vy;
9885     EX = new_ex;
9886     EY = new_ey;
9887     SXSIZE = new_sxsize;
9888     SYSIZE = new_sysize;
9889     DXSIZE = new_dxsize;
9890     DYSIZE = new_dysize;
9891     VXSIZE = new_vxsize;
9892     VYSIZE = new_vysize;
9893     EXSIZE = new_exsize;
9894     EYSIZE = new_eysize;
9895     REAL_SX = new_real_sx;
9896     REAL_SY = new_real_sy;
9897     FULL_SXSIZE = new_full_sxsize;
9898     FULL_SYSIZE = new_full_sysize;
9899     TILESIZE_VAR = new_tilesize_var;
9900
9901     init_gfx_buffers = TRUE;
9902     init_gadgets_and_anims = TRUE;
9903
9904     // Debug("tools:viewport", "viewports: init_gfx_buffers");
9905     // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9906   }
9907
9908   if (init_gfx_buffers)
9909   {
9910     // Debug("tools:viewport", "init_gfx_buffers");
9911
9912     SCR_FIELDX = new_scr_fieldx_buffers;
9913     SCR_FIELDY = new_scr_fieldy_buffers;
9914
9915     InitGfxBuffers();
9916
9917     SCR_FIELDX = new_scr_fieldx;
9918     SCR_FIELDY = new_scr_fieldy;
9919
9920     SetDrawDeactivationMask(REDRAW_NONE);
9921     SetDrawBackgroundMask(REDRAW_FIELD);
9922   }
9923
9924   if (init_video_buffer)
9925   {
9926     // Debug("tools:viewport", "init_video_buffer");
9927
9928     FreeAllImageTextures();     // needs old renderer to free the textures
9929
9930     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9931     InitImageTextures();
9932   }
9933
9934   if (init_gadgets_and_anims)
9935   {
9936     // Debug("tools:viewport", "init_gadgets_and_anims");
9937
9938     InitGadgets();
9939     InitGlobalAnimations();
9940   }
9941
9942   if (init_em_graphics)
9943   {
9944     InitGraphicInfo_EM();
9945   }
9946 }
9947
9948 void OpenURL(char *url)
9949 {
9950 #if SDL_VERSION_ATLEAST(2,0,14)
9951   SDL_OpenURL(url);
9952 #else
9953   Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9954        url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9955   Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9956 #endif
9957 }
9958
9959 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9960 {
9961   OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9962 }
9963
9964
9965 // ============================================================================
9966 // tests
9967 // ============================================================================
9968
9969 #if defined(PLATFORM_WINDOWS)
9970 /* FILETIME of Jan 1 1970 00:00:00. */
9971 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9972
9973 /*
9974  * timezone information is stored outside the kernel so tzp isn't used anymore.
9975  *
9976  * Note: this function is not for Win32 high precision timing purpose. See
9977  * elapsed_time().
9978  */
9979 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9980 {
9981   FILETIME    file_time;
9982   SYSTEMTIME  system_time;
9983   ULARGE_INTEGER ularge;
9984
9985   GetSystemTime(&system_time);
9986   SystemTimeToFileTime(&system_time, &file_time);
9987   ularge.LowPart = file_time.dwLowDateTime;
9988   ularge.HighPart = file_time.dwHighDateTime;
9989
9990   tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9991   tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9992
9993   return 0;
9994 }
9995 #endif
9996
9997 static char *test_init_uuid_random_function_simple(void)
9998 {
9999   static char seed_text[100];
10000   unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10001
10002   sprintf(seed_text, "%d", seed);
10003
10004   return seed_text;
10005 }
10006
10007 static char *test_init_uuid_random_function_better(void)
10008 {
10009   static char seed_text[100];
10010   struct timeval current_time;
10011
10012   gettimeofday(&current_time, NULL);
10013
10014   prng_seed_bytes(&current_time, sizeof(current_time));
10015
10016   sprintf(seed_text, "%ld.%ld",
10017           (long)current_time.tv_sec,
10018           (long)current_time.tv_usec);
10019
10020   return seed_text;
10021 }
10022
10023 #if defined(PLATFORM_WINDOWS)
10024 static char *test_init_uuid_random_function_better_windows(void)
10025 {
10026   static char seed_text[100];
10027   struct timeval current_time;
10028
10029   gettimeofday_windows(&current_time, NULL);
10030
10031   prng_seed_bytes(&current_time, sizeof(current_time));
10032
10033   sprintf(seed_text, "%ld.%ld",
10034           (long)current_time.tv_sec,
10035           (long)current_time.tv_usec);
10036
10037   return seed_text;
10038 }
10039 #endif
10040
10041 static unsigned int test_uuid_random_function_simple(int max)
10042 {
10043   return GetSimpleRandom(max);
10044 }
10045
10046 static unsigned int test_uuid_random_function_better(int max)
10047 {
10048   return (max > 0 ? prng_get_uint() % max : 0);
10049 }
10050
10051 #if defined(PLATFORM_WINDOWS)
10052 #define NUM_UUID_TESTS                  3
10053 #else
10054 #define NUM_UUID_TESTS                  2
10055 #endif
10056
10057 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10058 {
10059   struct hashtable *hash_seeds =
10060     create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10061   struct hashtable *hash_uuids =
10062     create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10063   static char message[100];
10064   int i;
10065
10066   char *random_name = (nr == 0 ? "simple" : "better");
10067   char *random_type = (always_seed ? "always" : "only once");
10068   char *(*init_random_function)(void) =
10069     (nr == 0 ?
10070      test_init_uuid_random_function_simple :
10071      test_init_uuid_random_function_better);
10072   unsigned int (*random_function)(int) =
10073     (nr == 0 ?
10074      test_uuid_random_function_simple :
10075      test_uuid_random_function_better);
10076   int xpos = 40;
10077
10078 #if defined(PLATFORM_WINDOWS)
10079   if (nr == 2)
10080   {
10081     random_name = "windows";
10082     init_random_function = test_init_uuid_random_function_better_windows;
10083   }
10084 #endif
10085
10086   ClearField();
10087
10088   DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10089   DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10090
10091   DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10092   DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10093   DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10094
10095   DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10096
10097   BackToFront();
10098
10099   // always initialize random number generator at least once
10100   init_random_function();
10101
10102   unsigned int time_start = SDL_GetTicks();
10103
10104   for (i = 0; i < num_uuids; i++)
10105   {
10106     if (always_seed)
10107     {
10108       char *seed = getStringCopy(init_random_function());
10109
10110       hashtable_remove(hash_seeds, seed);
10111       hashtable_insert(hash_seeds, seed, "1");
10112     }
10113
10114     char *uuid = getStringCopy(getUUIDExt(random_function));
10115
10116     hashtable_remove(hash_uuids, uuid);
10117     hashtable_insert(hash_uuids, uuid, "1");
10118   }
10119
10120   int num_unique_seeds = hashtable_count(hash_seeds);
10121   int num_unique_uuids = hashtable_count(hash_uuids);
10122
10123   unsigned int time_needed = SDL_GetTicks() - time_start;
10124
10125   DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10126
10127   DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10128
10129   if (always_seed)
10130     DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10131
10132   if (nr == NUM_UUID_TESTS - 1 && always_seed)
10133     DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10134   else
10135     DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10136
10137   sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10138
10139   Request(message, REQ_CONFIRM);
10140
10141   hashtable_destroy(hash_seeds, 0);
10142   hashtable_destroy(hash_uuids, 0);
10143 }
10144
10145 void TestGeneratingUUIDs(void)
10146 {
10147   int num_uuids = 1000000;
10148   int i, j;
10149
10150   for (i = 0; i < NUM_UUID_TESTS; i++)
10151     for (j = 0; j < 2; j++)
10152       TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10153
10154   CloseAllAndExit(0);
10155 }