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