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