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