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