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