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