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