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