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