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