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