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