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