improved performance of game actions loop in 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   else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4196   {
4197     // ------------------------------------------------------------------------
4198     // draw things behind the player, if needed
4199     // ------------------------------------------------------------------------
4200
4201     if (Back[jx][jy])
4202     {
4203       DrawLevelElement(jx, jy, Back[jx][jy]);
4204
4205       return;
4206     }
4207
4208     if (IS_ACTIVE_BOMB(element))
4209     {
4210       DrawLevelElement(jx, jy, EL_EMPTY);
4211
4212       return;
4213     }
4214
4215     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4216     {
4217       int old_element = GfxElement[jx][jy];
4218       int old_graphic = el_act_dir2img(old_element, action, move_dir);
4219       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4220
4221       if (GFX_CRUMBLED(old_element))
4222         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4223       else
4224         DrawScreenGraphic(sx, sy, old_graphic, frame);
4225
4226       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4227         static_player_is_opaque[pnr] = TRUE;
4228     }
4229     else
4230     {
4231       GfxElement[jx][jy] = EL_UNDEFINED;
4232
4233       // make sure that pushed elements are drawn with correct frame rate
4234       int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4235
4236       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4237         GfxFrame[jx][jy] = player->StepFrame;
4238
4239       DrawLevelField(jx, jy);
4240     }
4241   }
4242   else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4243   {
4244     // ------------------------------------------------------------------------
4245     // draw things the player is pushing, if needed
4246     // ------------------------------------------------------------------------
4247
4248     if (!player->is_pushing || !player->is_moving)
4249       return;
4250
4251     if (Tile[next_jx][next_jy] == EL_EXPLOSION)
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
4277     if (Back[next_jx][next_jy])
4278       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4279     else
4280       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4281
4282     int px = SCREENX(jx), py = SCREENY(jy);
4283     int pxx = (TILEX - ABS(sxx)) * dx;
4284     int pyy = (TILEY - ABS(syy)) * dy;
4285
4286 #if 1
4287     // do not draw (EM style) pushing animation when pushing is finished
4288     // (two-tile animations usually do not contain start and end frame)
4289     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4290       DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4291     else
4292       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4293 #else
4294     // masked drawing is needed for EMC style (double) movement graphics
4295     // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4296     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4297 #endif
4298   }
4299   else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4300   {
4301     // ------------------------------------------------------------------------
4302     // draw player himself
4303     // ------------------------------------------------------------------------
4304
4305     int graphic = getPlayerGraphic(player, move_dir);
4306
4307     // in the case of changed player action or direction, prevent the current
4308     // animation frame from being restarted for identical animations
4309     if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4310       player->Frame = last_player_frame;
4311
4312     int frame = getGraphicAnimationFrame(graphic, player->Frame);
4313
4314     if (player_is_opaque)
4315       DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4316     else
4317       DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4318
4319     if (SHIELD_ON(player))
4320     {
4321       graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4322                  IMG_SHIELD_NORMAL_ACTIVE);
4323       frame = getGraphicAnimationFrame(graphic, -1);
4324
4325       DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4326     }
4327   }
4328   else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4329   {
4330     // ------------------------------------------------------------------------
4331     // draw things in front of player (active dynamite or dynabombs)
4332     // ------------------------------------------------------------------------
4333
4334     if (IS_ACTIVE_BOMB(element))
4335     {
4336       int graphic = el2img(element);
4337       int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4338
4339       if (game.emulation == EMU_SUPAPLEX)
4340         DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4341       else
4342         DrawGraphicThruMask(sx, sy, graphic, frame);
4343     }
4344
4345     if (player_is_moving && last_element == EL_EXPLOSION)
4346     {
4347       int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4348                      GfxElement[last_jx][last_jy] :  EL_EMPTY);
4349       int graphic = el_act2img(element, ACTION_EXPLODING);
4350       int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4351       int phase = ExplodePhase[last_jx][last_jy] - 1;
4352       int frame = getGraphicAnimationFrame(graphic, phase - delay);
4353
4354       if (phase >= delay)
4355         DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4356     }
4357   }
4358   else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4359   {
4360     // ------------------------------------------------------------------------
4361     // draw elements the player is just walking/passing through/under
4362     // ------------------------------------------------------------------------
4363
4364     if (player_is_moving)
4365     {
4366       // handle the field the player is leaving ...
4367       if (IS_ACCESSIBLE_INSIDE(last_element))
4368         DrawLevelField(last_jx, last_jy);
4369       else if (IS_ACCESSIBLE_UNDER(last_element))
4370         DrawLevelFieldThruMask(last_jx, last_jy);
4371     }
4372
4373     // do not redraw accessible elements if the player is just pushing them
4374     if (!player_is_moving || !player->is_pushing)
4375     {
4376       // ... and the field the player is entering
4377       if (IS_ACCESSIBLE_INSIDE(element))
4378         DrawLevelField(jx, jy);
4379       else if (IS_ACCESSIBLE_UNDER(element))
4380         DrawLevelFieldThruMask(jx, jy);
4381     }
4382
4383     MarkTileDirty(sx, sy);
4384   }
4385 }
4386
4387 void DrawPlayer(struct PlayerInfo *player)
4388 {
4389   int i;
4390
4391   for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4392     DrawPlayerExt(player, i);
4393 }
4394
4395 void DrawAllPlayers(void)
4396 {
4397   int i, j;
4398
4399   for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4400     for (j = 0; j < MAX_PLAYERS; j++)
4401       if (stored_player[j].active)
4402         DrawPlayerExt(&stored_player[j], i);
4403 }
4404
4405 void DrawPlayerField(int x, int y)
4406 {
4407   if (!IS_PLAYER(x, y))
4408     return;
4409
4410   DrawPlayer(PLAYERINFO(x, y));
4411 }
4412
4413 // ----------------------------------------------------------------------------
4414
4415 void WaitForEventToContinue(void)
4416 {
4417   boolean first_wait = TRUE;
4418   boolean still_wait = TRUE;
4419
4420   if (program.headless)
4421     return;
4422
4423   // simulate releasing mouse button over last gadget, if still pressed
4424   if (button_status)
4425     HandleGadgets(-1, -1, 0);
4426
4427   button_status = MB_RELEASED;
4428
4429   ClearEventQueue();
4430   ClearPlayerAction();
4431
4432   while (still_wait)
4433   {
4434     Event event;
4435
4436     if (NextValidEvent(&event))
4437     {
4438       switch (event.type)
4439       {
4440         case EVENT_BUTTONPRESS:
4441         case EVENT_FINGERPRESS:
4442           first_wait = FALSE;
4443           break;
4444
4445         case EVENT_BUTTONRELEASE:
4446         case EVENT_FINGERRELEASE:
4447           still_wait = first_wait;
4448           break;
4449
4450         case EVENT_KEYPRESS:
4451         case SDL_CONTROLLERBUTTONDOWN:
4452         case SDL_JOYBUTTONDOWN:
4453           still_wait = FALSE;
4454           break;
4455
4456         default:
4457           HandleOtherEvents(&event);
4458           break;
4459       }
4460     }
4461     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4462     {
4463       still_wait = FALSE;
4464     }
4465
4466     if (!PendingEvent())
4467       BackToFront();
4468   }
4469 }
4470
4471 #define MAX_REQUEST_LINES               13
4472 #define MAX_REQUEST_LINE_FONT1_LEN      7
4473 #define MAX_REQUEST_LINE_FONT2_LEN      10
4474
4475 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4476 {
4477   boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4478                              checkGameEnded());
4479   int draw_buffer_last = GetDrawtoField();
4480   int width  = request.width;
4481   int height = request.height;
4482   int sx, sy;
4483   int result;
4484
4485   // when showing request dialog after game ended, deactivate game panel
4486   if (game_just_ended)
4487     game.panel.active = FALSE;
4488
4489   game.request_active = TRUE;
4490
4491   setRequestPosition(&sx, &sy, FALSE);
4492
4493   button_status = MB_RELEASED;
4494
4495   request_gadget_id = -1;
4496   result = -1;
4497
4498   while (result < 0)
4499   {
4500     boolean event_handled = FALSE;
4501
4502     if (game_just_ended)
4503     {
4504       SetDrawtoField(draw_buffer_game);
4505
4506       HandleGameActions();
4507
4508       SetDrawtoField(DRAW_TO_BACKBUFFER);
4509
4510       if (global.use_envelope_request)
4511       {
4512         // copy current state of request area to middle of playfield area
4513         BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4514       }
4515     }
4516
4517     if (PendingEvent())
4518     {
4519       Event event;
4520
4521       while (NextValidEvent(&event))
4522       {
4523         event_handled = TRUE;
4524
4525         switch (event.type)
4526         {
4527           case EVENT_BUTTONPRESS:
4528           case EVENT_BUTTONRELEASE:
4529           case EVENT_MOTIONNOTIFY:
4530           {
4531             int mx, my;
4532
4533             if (event.type == EVENT_MOTIONNOTIFY)
4534             {
4535               if (!button_status)
4536                 continue;
4537
4538               motion_status = TRUE;
4539               mx = ((MotionEvent *) &event)->x;
4540               my = ((MotionEvent *) &event)->y;
4541             }
4542             else
4543             {
4544               motion_status = FALSE;
4545               mx = ((ButtonEvent *) &event)->x;
4546               my = ((ButtonEvent *) &event)->y;
4547               if (event.type == EVENT_BUTTONPRESS)
4548                 button_status = ((ButtonEvent *) &event)->button;
4549               else
4550                 button_status = MB_RELEASED;
4551             }
4552
4553             // this sets 'request_gadget_id'
4554             HandleGadgets(mx, my, button_status);
4555
4556             switch (request_gadget_id)
4557             {
4558               case TOOL_CTRL_ID_YES:
4559               case TOOL_CTRL_ID_TOUCH_YES:
4560                 result = TRUE;
4561                 break;
4562               case TOOL_CTRL_ID_NO:
4563               case TOOL_CTRL_ID_TOUCH_NO:
4564                 result = FALSE;
4565                 break;
4566               case TOOL_CTRL_ID_CONFIRM:
4567               case TOOL_CTRL_ID_TOUCH_CONFIRM:
4568                 result = TRUE | FALSE;
4569                 break;
4570
4571               case TOOL_CTRL_ID_PLAYER_1:
4572                 result = 1;
4573                 break;
4574               case TOOL_CTRL_ID_PLAYER_2:
4575                 result = 2;
4576                 break;
4577               case TOOL_CTRL_ID_PLAYER_3:
4578                 result = 3;
4579                 break;
4580               case TOOL_CTRL_ID_PLAYER_4:
4581                 result = 4;
4582                 break;
4583
4584               default:
4585                 // only check clickable animations if no request gadget clicked
4586                 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4587                 break;
4588             }
4589
4590             break;
4591           }
4592
4593           case SDL_WINDOWEVENT:
4594             HandleWindowEvent((WindowEvent *) &event);
4595             break;
4596
4597           case SDL_APP_WILLENTERBACKGROUND:
4598           case SDL_APP_DIDENTERBACKGROUND:
4599           case SDL_APP_WILLENTERFOREGROUND:
4600           case SDL_APP_DIDENTERFOREGROUND:
4601             HandlePauseResumeEvent((PauseResumeEvent *) &event);
4602             break;
4603
4604           case EVENT_KEYPRESS:
4605           {
4606             Key key = GetEventKey((KeyEvent *)&event);
4607
4608             switch (key)
4609             {
4610               case KSYM_space:
4611                 if (req_state & REQ_CONFIRM)
4612                   result = 1;
4613                 break;
4614
4615               case KSYM_Return:
4616               case KSYM_y:
4617               case KSYM_Y:
4618               case KSYM_Select:
4619               case KSYM_Menu:
4620 #if defined(KSYM_Rewind)
4621               case KSYM_Rewind:         // for Amazon Fire TV remote
4622 #endif
4623                 result = 1;
4624                 break;
4625
4626               case KSYM_Escape:
4627               case KSYM_n:
4628               case KSYM_N:
4629               case KSYM_Back:
4630 #if defined(KSYM_FastForward)
4631               case KSYM_FastForward:    // for Amazon Fire TV remote
4632 #endif
4633                 result = 0;
4634                 break;
4635
4636               default:
4637                 HandleKeysDebug(key, KEY_PRESSED);
4638                 break;
4639             }
4640
4641             if (req_state & REQ_PLAYER)
4642             {
4643               int old_player_nr = setup.network_player_nr;
4644
4645               if (result != -1)
4646                 result = old_player_nr + 1;
4647
4648               switch (key)
4649               {
4650                 case KSYM_space:
4651                   result = old_player_nr + 1;
4652                   break;
4653
4654                 case KSYM_Up:
4655                 case KSYM_1:
4656                   result = 1;
4657                   break;
4658
4659                 case KSYM_Right:
4660                 case KSYM_2:
4661                   result = 2;
4662                   break;
4663
4664                 case KSYM_Down:
4665                 case KSYM_3:
4666                   result = 3;
4667                   break;
4668
4669                 case KSYM_Left:
4670                 case KSYM_4:
4671                   result = 4;
4672                   break;
4673
4674                 default:
4675                   break;
4676               }
4677             }
4678
4679             break;
4680           }
4681
4682           case EVENT_FINGERRELEASE:
4683           case EVENT_KEYRELEASE:
4684             ClearPlayerAction();
4685             break;
4686
4687           case SDL_CONTROLLERBUTTONDOWN:
4688             switch (event.cbutton.button)
4689             {
4690               case SDL_CONTROLLER_BUTTON_A:
4691               case SDL_CONTROLLER_BUTTON_X:
4692               case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4693               case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4694                 result = 1;
4695                 break;
4696
4697               case SDL_CONTROLLER_BUTTON_B:
4698               case SDL_CONTROLLER_BUTTON_Y:
4699               case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4700               case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4701               case SDL_CONTROLLER_BUTTON_BACK:
4702                 result = 0;
4703                 break;
4704             }
4705
4706             if (req_state & REQ_PLAYER)
4707             {
4708               int old_player_nr = setup.network_player_nr;
4709
4710               if (result != -1)
4711                 result = old_player_nr + 1;
4712
4713               switch (event.cbutton.button)
4714               {
4715                 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4716                 case SDL_CONTROLLER_BUTTON_Y:
4717                   result = 1;
4718                   break;
4719
4720                 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4721                 case SDL_CONTROLLER_BUTTON_B:
4722                   result = 2;
4723                   break;
4724
4725                 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4726                 case SDL_CONTROLLER_BUTTON_A:
4727                   result = 3;
4728                   break;
4729
4730                 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4731                 case SDL_CONTROLLER_BUTTON_X:
4732                   result = 4;
4733                   break;
4734
4735                 default:
4736                   break;
4737               }
4738             }
4739
4740             break;
4741
4742           case SDL_CONTROLLERBUTTONUP:
4743             HandleJoystickEvent(&event);
4744             ClearPlayerAction();
4745             break;
4746
4747           default:
4748             HandleOtherEvents(&event);
4749             break;
4750         }
4751       }
4752     }
4753     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4754     {
4755       int joy = AnyJoystick();
4756
4757       if (joy & JOY_BUTTON_1)
4758         result = 1;
4759       else if (joy & JOY_BUTTON_2)
4760         result = 0;
4761     }
4762     else if (AnyJoystick())
4763     {
4764       int joy = AnyJoystick();
4765
4766       if (req_state & REQ_PLAYER)
4767       {
4768         if (joy & JOY_UP)
4769           result = 1;
4770         else if (joy & JOY_RIGHT)
4771           result = 2;
4772         else if (joy & JOY_DOWN)
4773           result = 3;
4774         else if (joy & JOY_LEFT)
4775           result = 4;
4776       }
4777     }
4778
4779     if (event_handled)
4780     {
4781       if (game_just_ended)
4782       {
4783         if (global.use_envelope_request)
4784         {
4785           // copy back current state of pressed buttons inside request area
4786           BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4787         }
4788       }
4789
4790       PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4791     }
4792
4793     BackToFront();
4794   }
4795
4796   SetDrawtoField(draw_buffer_last);
4797
4798   game.request_active = FALSE;
4799
4800   return result;
4801 }
4802
4803 static boolean RequestDoor(char *text, unsigned int req_state)
4804 {
4805   int draw_buffer_last = GetDrawtoField();
4806   unsigned int old_door_state;
4807   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4808   int font_nr = FONT_TEXT_2;
4809   char *text_ptr;
4810   int result;
4811   int ty;
4812
4813   if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4814   {
4815     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4816     font_nr = FONT_TEXT_1;
4817   }
4818
4819   if (game_status == GAME_MODE_PLAYING)
4820     BlitScreenToBitmap(backbuffer);
4821
4822   // disable deactivated drawing when quick-loading level tape recording
4823   if (tape.playing && tape.deactivate_display)
4824     TapeDeactivateDisplayOff(TRUE);
4825
4826   SetMouseCursor(CURSOR_DEFAULT);
4827
4828   // pause network game while waiting for request to answer
4829   if (network.enabled &&
4830       game_status == GAME_MODE_PLAYING &&
4831       !game.all_players_gone &&
4832       req_state & REQUEST_WAIT_FOR_INPUT)
4833     SendToServer_PausePlaying();
4834
4835   old_door_state = GetDoorState();
4836
4837   // simulate releasing mouse button over last gadget, if still pressed
4838   if (button_status)
4839     HandleGadgets(-1, -1, 0);
4840
4841   UnmapAllGadgets();
4842
4843   // draw released gadget before proceeding
4844   // BackToFront();
4845
4846   if (old_door_state & DOOR_OPEN_1)
4847   {
4848     CloseDoor(DOOR_CLOSE_1);
4849
4850     // save old door content
4851     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4852                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4853   }
4854
4855   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4856   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4857
4858   // clear door drawing field
4859   DrawBackground(DX, DY, DXSIZE, DYSIZE);
4860
4861   // force DOOR font inside door area
4862   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4863
4864   // write text for request
4865   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4866   {
4867     char text_line[max_request_line_len + 1];
4868     int tx, tl, tc = 0;
4869
4870     if (!*text_ptr)
4871       break;
4872
4873     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4874     {
4875       tc = *(text_ptr + tx);
4876       // if (!tc || tc == ' ')
4877       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4878         break;
4879     }
4880
4881     if ((tc == '?' || tc == '!') && tl == 0)
4882       tl = 1;
4883
4884     if (!tl)
4885     { 
4886       text_ptr++; 
4887       ty--; 
4888       continue; 
4889     }
4890
4891     strncpy(text_line, text_ptr, tl);
4892     text_line[tl] = 0;
4893
4894     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4895              DY + 8 + ty * (getFontHeight(font_nr) + 2),
4896              text_line, font_nr);
4897
4898     text_ptr += tl + (tc == ' ' ? 1 : 0);
4899     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4900   }
4901
4902   ResetFontStatus();
4903
4904   if (req_state & REQ_ASK)
4905   {
4906     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4907     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4908     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4909     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4910   }
4911   else if (req_state & REQ_CONFIRM)
4912   {
4913     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4914     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4915   }
4916   else if (req_state & REQ_PLAYER)
4917   {
4918     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4919     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4920     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4921     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4922   }
4923
4924   // copy request gadgets to door backbuffer
4925   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4926
4927   OpenDoor(DOOR_OPEN_1);
4928
4929   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4930   {
4931     if (game_status == GAME_MODE_PLAYING)
4932     {
4933       SetPanelBackground();
4934       SetDrawBackgroundMask(REDRAW_DOOR_1);
4935     }
4936     else
4937     {
4938       SetDrawBackgroundMask(REDRAW_FIELD);
4939     }
4940
4941     return FALSE;
4942   }
4943
4944   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4945
4946   // ---------- handle request buttons ----------
4947   result = RequestHandleEvents(req_state, draw_buffer_last);
4948
4949   UnmapToolButtons();
4950
4951   if (!(req_state & REQ_STAY_OPEN))
4952   {
4953     CloseDoor(DOOR_CLOSE_1);
4954
4955     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4956         (req_state & REQ_REOPEN))
4957       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4958   }
4959
4960   RemapAllGadgets();
4961
4962   if (game_status == GAME_MODE_PLAYING)
4963   {
4964     SetPanelBackground();
4965     SetDrawBackgroundMask(REDRAW_DOOR_1);
4966   }
4967   else
4968   {
4969     SetDrawBackgroundMask(REDRAW_FIELD);
4970   }
4971
4972   // continue network game after request
4973   if (network.enabled &&
4974       game_status == GAME_MODE_PLAYING &&
4975       !game.all_players_gone &&
4976       req_state & REQUEST_WAIT_FOR_INPUT)
4977     SendToServer_ContinuePlaying();
4978
4979   // restore deactivated drawing when quick-loading level tape recording
4980   if (tape.playing && tape.deactivate_display)
4981     TapeDeactivateDisplayOn();
4982
4983   return result;
4984 }
4985
4986 static boolean RequestEnvelope(char *text, unsigned int req_state)
4987 {
4988   int draw_buffer_last = GetDrawtoField();
4989   int result;
4990
4991   if (game_status == GAME_MODE_PLAYING)
4992     BlitScreenToBitmap(backbuffer);
4993
4994   // disable deactivated drawing when quick-loading level tape recording
4995   if (tape.playing && tape.deactivate_display)
4996     TapeDeactivateDisplayOff(TRUE);
4997
4998   SetMouseCursor(CURSOR_DEFAULT);
4999
5000   // pause network game while waiting for request to answer
5001   if (network.enabled &&
5002       game_status == GAME_MODE_PLAYING &&
5003       !game.all_players_gone &&
5004       req_state & REQUEST_WAIT_FOR_INPUT)
5005     SendToServer_PausePlaying();
5006
5007   // simulate releasing mouse button over last gadget, if still pressed
5008   if (button_status)
5009     HandleGadgets(-1, -1, 0);
5010
5011   UnmapAllGadgets();
5012
5013   // (replace with setting corresponding request background)
5014   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5015   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5016
5017   // clear door drawing field
5018   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5019
5020   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5021
5022   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5023   {
5024     if (game_status == GAME_MODE_PLAYING)
5025     {
5026       SetPanelBackground();
5027       SetDrawBackgroundMask(REDRAW_DOOR_1);
5028     }
5029     else
5030     {
5031       SetDrawBackgroundMask(REDRAW_FIELD);
5032     }
5033
5034     return FALSE;
5035   }
5036
5037   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5038
5039   // ---------- handle request buttons ----------
5040   result = RequestHandleEvents(req_state, draw_buffer_last);
5041
5042   UnmapToolButtons();
5043
5044   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5045
5046   RemapAllGadgets();
5047
5048   if (game_status == GAME_MODE_PLAYING)
5049   {
5050     SetPanelBackground();
5051     SetDrawBackgroundMask(REDRAW_DOOR_1);
5052   }
5053   else
5054   {
5055     SetDrawBackgroundMask(REDRAW_FIELD);
5056   }
5057
5058   // continue network game after request
5059   if (network.enabled &&
5060       game_status == GAME_MODE_PLAYING &&
5061       !game.all_players_gone &&
5062       req_state & REQUEST_WAIT_FOR_INPUT)
5063     SendToServer_ContinuePlaying();
5064
5065   // restore deactivated drawing when quick-loading level tape recording
5066   if (tape.playing && tape.deactivate_display)
5067     TapeDeactivateDisplayOn();
5068
5069   return result;
5070 }
5071
5072 boolean Request(char *text, unsigned int req_state)
5073 {
5074   boolean overlay_enabled = GetOverlayEnabled();
5075   boolean result;
5076
5077   game.request_active_or_moving = TRUE;
5078
5079   SetOverlayEnabled(FALSE);
5080
5081   if (global.use_envelope_request)
5082     result = RequestEnvelope(text, req_state);
5083   else
5084     result = RequestDoor(text, req_state);
5085
5086   SetOverlayEnabled(overlay_enabled);
5087
5088   game.request_active_or_moving = FALSE;
5089
5090   return result;
5091 }
5092
5093 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5094 {
5095   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5096   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5097   int compare_result;
5098
5099   if (dpo1->sort_priority != dpo2->sort_priority)
5100     compare_result = dpo1->sort_priority - dpo2->sort_priority;
5101   else
5102     compare_result = dpo1->nr - dpo2->nr;
5103
5104   return compare_result;
5105 }
5106
5107 void InitGraphicCompatibilityInfo_Doors(void)
5108 {
5109   struct
5110   {
5111     int door_token;
5112     int part_1, part_8;
5113     struct DoorInfo *door;
5114   }
5115   doors[] =
5116   {
5117     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
5118     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
5119
5120     { -1,       -1,                     -1,                     NULL    }
5121   };
5122   struct Rect door_rect_list[] =
5123   {
5124     { DX, DY, DXSIZE, DYSIZE },
5125     { VX, VY, VXSIZE, VYSIZE }
5126   };
5127   int i, j;
5128
5129   for (i = 0; doors[i].door_token != -1; i++)
5130   {
5131     int door_token = doors[i].door_token;
5132     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5133     int part_1 = doors[i].part_1;
5134     int part_8 = doors[i].part_8;
5135     int part_2 = part_1 + 1;
5136     int part_3 = part_1 + 2;
5137     struct DoorInfo *door = doors[i].door;
5138     struct Rect *door_rect = &door_rect_list[door_index];
5139     boolean door_gfx_redefined = FALSE;
5140
5141     // check if any door part graphic definitions have been redefined
5142
5143     for (j = 0; door_part_controls[j].door_token != -1; j++)
5144     {
5145       struct DoorPartControlInfo *dpc = &door_part_controls[j];
5146       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5147
5148       if (dpc->door_token == door_token && fi->redefined)
5149         door_gfx_redefined = TRUE;
5150     }
5151
5152     // check for old-style door graphic/animation modifications
5153
5154     if (!door_gfx_redefined)
5155     {
5156       if (door->anim_mode & ANIM_STATIC_PANEL)
5157       {
5158         door->panel.step_xoffset = 0;
5159         door->panel.step_yoffset = 0;
5160       }
5161
5162       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5163       {
5164         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5165         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5166         int num_door_steps, num_panel_steps;
5167
5168         // remove door part graphics other than the two default wings
5169
5170         for (j = 0; door_part_controls[j].door_token != -1; j++)
5171         {
5172           struct DoorPartControlInfo *dpc = &door_part_controls[j];
5173           struct GraphicInfo *g = &graphic_info[dpc->graphic];
5174
5175           if (dpc->graphic >= part_3 &&
5176               dpc->graphic <= part_8)
5177             g->bitmap = NULL;
5178         }
5179
5180         // set graphics and screen positions of the default wings
5181
5182         g_part_1->width  = door_rect->width;
5183         g_part_1->height = door_rect->height;
5184         g_part_2->width  = door_rect->width;
5185         g_part_2->height = door_rect->height;
5186         g_part_2->src_x = door_rect->width;
5187         g_part_2->src_y = g_part_1->src_y;
5188
5189         door->part_2.x = door->part_1.x;
5190         door->part_2.y = door->part_1.y;
5191
5192         if (door->width != -1)
5193         {
5194           g_part_1->width = door->width;
5195           g_part_2->width = door->width;
5196
5197           // special treatment for graphics and screen position of right wing
5198           g_part_2->src_x += door_rect->width - door->width;
5199           door->part_2.x  += door_rect->width - door->width;
5200         }
5201
5202         if (door->height != -1)
5203         {
5204           g_part_1->height = door->height;
5205           g_part_2->height = door->height;
5206
5207           // special treatment for graphics and screen position of bottom wing
5208           g_part_2->src_y += door_rect->height - door->height;
5209           door->part_2.y  += door_rect->height - door->height;
5210         }
5211
5212         // set animation delays for the default wings and panels
5213
5214         door->part_1.step_delay = door->step_delay;
5215         door->part_2.step_delay = door->step_delay;
5216         door->panel.step_delay  = door->step_delay;
5217
5218         // set animation draw order for the default wings
5219
5220         door->part_1.sort_priority = 2; // draw left wing over ...
5221         door->part_2.sort_priority = 1; //          ... right wing
5222
5223         // set animation draw offset for the default wings
5224
5225         if (door->anim_mode & ANIM_HORIZONTAL)
5226         {
5227           door->part_1.step_xoffset = door->step_offset;
5228           door->part_1.step_yoffset = 0;
5229           door->part_2.step_xoffset = door->step_offset * -1;
5230           door->part_2.step_yoffset = 0;
5231
5232           num_door_steps = g_part_1->width / door->step_offset;
5233         }
5234         else    // ANIM_VERTICAL
5235         {
5236           door->part_1.step_xoffset = 0;
5237           door->part_1.step_yoffset = door->step_offset;
5238           door->part_2.step_xoffset = 0;
5239           door->part_2.step_yoffset = door->step_offset * -1;
5240
5241           num_door_steps = g_part_1->height / door->step_offset;
5242         }
5243
5244         // set animation draw offset for the default panels
5245
5246         if (door->step_offset > 1)
5247         {
5248           num_panel_steps = 2 * door_rect->height / door->step_offset;
5249           door->panel.start_step = num_panel_steps - num_door_steps;
5250           door->panel.start_step_closing = door->panel.start_step;
5251         }
5252         else
5253         {
5254           num_panel_steps = door_rect->height / door->step_offset;
5255           door->panel.start_step = num_panel_steps - num_door_steps / 2;
5256           door->panel.start_step_closing = door->panel.start_step;
5257           door->panel.step_delay *= 2;
5258         }
5259       }
5260     }
5261   }
5262 }
5263
5264 void InitDoors(void)
5265 {
5266   int i;
5267
5268   for (i = 0; door_part_controls[i].door_token != -1; i++)
5269   {
5270     struct DoorPartControlInfo *dpc = &door_part_controls[i];
5271     struct DoorPartOrderInfo *dpo = &door_part_order[i];
5272
5273     // initialize "start_step_opening" and "start_step_closing", if needed
5274     if (dpc->pos->start_step_opening == 0 &&
5275         dpc->pos->start_step_closing == 0)
5276     {
5277       // dpc->pos->start_step_opening = dpc->pos->start_step;
5278       dpc->pos->start_step_closing = dpc->pos->start_step;
5279     }
5280
5281     // fill structure for door part draw order (sorted below)
5282     dpo->nr = i;
5283     dpo->sort_priority = dpc->pos->sort_priority;
5284   }
5285
5286   // sort door part controls according to sort_priority and graphic number
5287   qsort(door_part_order, MAX_DOOR_PARTS,
5288         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5289 }
5290
5291 unsigned int OpenDoor(unsigned int door_state)
5292 {
5293   if (door_state & DOOR_COPY_BACK)
5294   {
5295     if (door_state & DOOR_OPEN_1)
5296       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5297                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5298
5299     if (door_state & DOOR_OPEN_2)
5300       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5301                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5302
5303     door_state &= ~DOOR_COPY_BACK;
5304   }
5305
5306   return MoveDoor(door_state);
5307 }
5308
5309 unsigned int CloseDoor(unsigned int door_state)
5310 {
5311   unsigned int old_door_state = GetDoorState();
5312
5313   if (!(door_state & DOOR_NO_COPY_BACK))
5314   {
5315     if (old_door_state & DOOR_OPEN_1)
5316       BlitBitmap(backbuffer, bitmap_db_door_1,
5317                  DX, DY, DXSIZE, DYSIZE, 0, 0);
5318
5319     if (old_door_state & DOOR_OPEN_2)
5320       BlitBitmap(backbuffer, bitmap_db_door_2,
5321                  VX, VY, VXSIZE, VYSIZE, 0, 0);
5322
5323     door_state &= ~DOOR_NO_COPY_BACK;
5324   }
5325
5326   return MoveDoor(door_state);
5327 }
5328
5329 unsigned int GetDoorState(void)
5330 {
5331   return MoveDoor(DOOR_GET_STATE);
5332 }
5333
5334 unsigned int SetDoorState(unsigned int door_state)
5335 {
5336   return MoveDoor(door_state | DOOR_SET_STATE);
5337 }
5338
5339 static int euclid(int a, int b)
5340 {
5341   return (b ? euclid(b, a % b) : a);
5342 }
5343
5344 unsigned int MoveDoor(unsigned int door_state)
5345 {
5346   struct Rect door_rect_list[] =
5347   {
5348     { DX, DY, DXSIZE, DYSIZE },
5349     { VX, VY, VXSIZE, VYSIZE }
5350   };
5351   static int door1 = DOOR_CLOSE_1;
5352   static int door2 = DOOR_CLOSE_2;
5353   DelayCounter door_delay = { 0 };
5354   int i;
5355
5356   if (door_state == DOOR_GET_STATE)
5357     return (door1 | door2);
5358
5359   if (door_state & DOOR_SET_STATE)
5360   {
5361     if (door_state & DOOR_ACTION_1)
5362       door1 = door_state & DOOR_ACTION_1;
5363     if (door_state & DOOR_ACTION_2)
5364       door2 = door_state & DOOR_ACTION_2;
5365
5366     return (door1 | door2);
5367   }
5368
5369   if (!(door_state & DOOR_FORCE_REDRAW))
5370   {
5371     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5372       door_state &= ~DOOR_OPEN_1;
5373     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5374       door_state &= ~DOOR_CLOSE_1;
5375     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5376       door_state &= ~DOOR_OPEN_2;
5377     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5378       door_state &= ~DOOR_CLOSE_2;
5379   }
5380
5381   if (global.autoplay_leveldir)
5382   {
5383     door_state |= DOOR_NO_DELAY;
5384     door_state &= ~DOOR_CLOSE_ALL;
5385   }
5386
5387   if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5388     door_state |= DOOR_NO_DELAY;
5389
5390   if (door_state & DOOR_ACTION)
5391   {
5392     boolean door_panel_drawn[NUM_DOORS];
5393     boolean panel_has_doors[NUM_DOORS];
5394     boolean door_part_skip[MAX_DOOR_PARTS];
5395     boolean door_part_done[MAX_DOOR_PARTS];
5396     boolean door_part_done_all;
5397     int num_steps[MAX_DOOR_PARTS];
5398     int max_move_delay = 0;     // delay for complete animations of all doors
5399     int max_step_delay = 0;     // delay (ms) between two animation frames
5400     int num_move_steps = 0;     // number of animation steps for all doors
5401     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
5402     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
5403     int start = 0;
5404     int k;
5405
5406     for (i = 0; i < NUM_DOORS; i++)
5407       panel_has_doors[i] = FALSE;
5408
5409     for (i = 0; i < MAX_DOOR_PARTS; i++)
5410     {
5411       struct DoorPartControlInfo *dpc = &door_part_controls[i];
5412       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5413       int door_token = dpc->door_token;
5414
5415       door_part_done[i] = FALSE;
5416       door_part_skip[i] = (!(door_state & door_token) ||
5417                            !g->bitmap);
5418     }
5419
5420     for (i = 0; i < MAX_DOOR_PARTS; i++)
5421     {
5422       int nr = door_part_order[i].nr;
5423       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5424       struct DoorPartPosInfo *pos = dpc->pos;
5425       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5426       int door_token = dpc->door_token;
5427       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5428       boolean is_panel = DOOR_PART_IS_PANEL(nr);
5429       int step_xoffset = ABS(pos->step_xoffset);
5430       int step_yoffset = ABS(pos->step_yoffset);
5431       int step_delay = pos->step_delay;
5432       int current_door_state = door_state & door_token;
5433       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5434       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5435       boolean part_opening = (is_panel ? door_closing : door_opening);
5436       int start_step = (part_opening ? pos->start_step_opening :
5437                         pos->start_step_closing);
5438       float move_xsize = (step_xoffset ? g->width  : 0);
5439       float move_ysize = (step_yoffset ? g->height : 0);
5440       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5441       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5442       int move_steps = (move_xsteps && move_ysteps ?
5443                         MIN(move_xsteps, move_ysteps) :
5444                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
5445       int move_delay = move_steps * step_delay;
5446
5447       if (door_part_skip[nr])
5448         continue;
5449
5450       max_move_delay = MAX(max_move_delay, move_delay);
5451       max_step_delay = (max_step_delay == 0 ? step_delay :
5452                         euclid(max_step_delay, step_delay));
5453       num_steps[nr] = move_steps;
5454
5455       if (!is_panel)
5456       {
5457         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5458
5459         panel_has_doors[door_index] = TRUE;
5460       }
5461     }
5462
5463     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
5464
5465     num_move_steps = max_move_delay / max_step_delay;
5466     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5467
5468     door_delay.value = max_step_delay;
5469
5470     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5471     {
5472       start = num_move_steps - 1;
5473     }
5474     else
5475     {
5476       // opening door sound has priority over simultaneously closing door
5477       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5478       {
5479         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5480
5481         if (door_state & DOOR_OPEN_1)
5482           PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5483         if (door_state & DOOR_OPEN_2)
5484           PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5485       }
5486       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5487       {
5488         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5489
5490         if (door_state & DOOR_CLOSE_1)
5491           PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5492         if (door_state & DOOR_CLOSE_2)
5493           PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5494       }
5495     }
5496
5497     for (k = start; k < num_move_steps; k++)
5498     {
5499       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
5500
5501       door_part_done_all = TRUE;
5502
5503       for (i = 0; i < NUM_DOORS; i++)
5504         door_panel_drawn[i] = FALSE;
5505
5506       for (i = 0; i < MAX_DOOR_PARTS; i++)
5507       {
5508         int nr = door_part_order[i].nr;
5509         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5510         struct DoorPartPosInfo *pos = dpc->pos;
5511         struct GraphicInfo *g = &graphic_info[dpc->graphic];
5512         int door_token = dpc->door_token;
5513         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5514         boolean is_panel = DOOR_PART_IS_PANEL(nr);
5515         boolean is_panel_and_door_has_closed = FALSE;
5516         struct Rect *door_rect = &door_rect_list[door_index];
5517         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5518                                   bitmap_db_door_2);
5519         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5520         int current_door_state = door_state & door_token;
5521         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5522         boolean door_closing = !door_opening;
5523         boolean part_opening = (is_panel ? door_closing : door_opening);
5524         boolean part_closing = !part_opening;
5525         int start_step = (part_opening ? pos->start_step_opening :
5526                           pos->start_step_closing);
5527         int step_delay = pos->step_delay;
5528         int step_factor = step_delay / max_step_delay;
5529         int k1 = (step_factor ? k / step_factor + 1 : k);
5530         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5531         int kk = MAX(0, k2);
5532         int g_src_x = 0;
5533         int g_src_y = 0;
5534         int src_x, src_y, src_xx, src_yy;
5535         int dst_x, dst_y, dst_xx, dst_yy;
5536         int width, height;
5537
5538         if (door_part_skip[nr])
5539           continue;
5540
5541         if (!(door_state & door_token))
5542           continue;
5543
5544         if (!g->bitmap)
5545           continue;
5546
5547         if (!is_panel)
5548         {
5549           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5550           int kk_door = MAX(0, k2_door);
5551           int sync_frame = kk_door * door_delay.value;
5552           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5553
5554           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5555                                 &g_src_x, &g_src_y);
5556         }
5557
5558         // draw door panel
5559
5560         if (!door_panel_drawn[door_index])
5561         {
5562           ClearRectangle(drawto, door_rect->x, door_rect->y,
5563                          door_rect->width, door_rect->height);
5564
5565           door_panel_drawn[door_index] = TRUE;
5566         }
5567
5568         // draw opening or closing door parts
5569
5570         if (pos->step_xoffset < 0)      // door part on right side
5571         {
5572           src_xx = 0;
5573           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5574           width = g->width;
5575
5576           if (dst_xx + width > door_rect->width)
5577             width = door_rect->width - dst_xx;
5578         }
5579         else                            // door part on left side
5580         {
5581           src_xx = 0;
5582           dst_xx = pos->x - kk * pos->step_xoffset;
5583
5584           if (dst_xx < 0)
5585           {
5586             src_xx = ABS(dst_xx);
5587             dst_xx = 0;
5588           }
5589
5590           width = g->width - src_xx;
5591
5592           if (width > door_rect->width)
5593             width = door_rect->width;
5594
5595           // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5596         }
5597
5598         if (pos->step_yoffset < 0)      // door part on bottom side
5599         {
5600           src_yy = 0;
5601           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5602           height = g->height;
5603
5604           if (dst_yy + height > door_rect->height)
5605             height = door_rect->height - dst_yy;
5606         }
5607         else                            // door part on top side
5608         {
5609           src_yy = 0;
5610           dst_yy = pos->y - kk * pos->step_yoffset;
5611
5612           if (dst_yy < 0)
5613           {
5614             src_yy = ABS(dst_yy);
5615             dst_yy = 0;
5616           }
5617
5618           height = g->height - src_yy;
5619         }
5620
5621         src_x = g_src_x + src_xx;
5622         src_y = g_src_y + src_yy;
5623
5624         dst_x = door_rect->x + dst_xx;
5625         dst_y = door_rect->y + dst_yy;
5626
5627         is_panel_and_door_has_closed =
5628           (is_panel &&
5629            door_closing &&
5630            panel_has_doors[door_index] &&
5631            k >= num_move_steps_doors_only - 1);
5632
5633         if (width  >= 0 && width  <= g->width &&
5634             height >= 0 && height <= g->height &&
5635             !is_panel_and_door_has_closed)
5636         {
5637           if (is_panel || !pos->draw_masked)
5638             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5639                        dst_x, dst_y);
5640           else
5641             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5642                              dst_x, dst_y);
5643         }
5644
5645         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5646
5647         if ((part_opening && (width < 0         || height < 0)) ||
5648             (part_closing && (width >= g->width && height >= g->height)))
5649           door_part_done[nr] = TRUE;
5650
5651         // continue door part animations, but not panel after door has closed
5652         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5653           door_part_done_all = FALSE;
5654       }
5655
5656       if (!(door_state & DOOR_NO_DELAY))
5657       {
5658         BackToFront();
5659
5660         SkipUntilDelayReached(&door_delay, &k, last_frame);
5661
5662         // prevent OS (Windows) from complaining about program not responding
5663         CheckQuitEvent();
5664       }
5665
5666       if (door_part_done_all)
5667         break;
5668     }
5669
5670     if (!(door_state & DOOR_NO_DELAY))
5671     {
5672       // wait for specified door action post delay
5673       if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5674         door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5675       else if (door_state & DOOR_ACTION_1)
5676         door_delay.value = door_1.post_delay;
5677       else if (door_state & DOOR_ACTION_2)
5678         door_delay.value = door_2.post_delay;
5679
5680       while (!DelayReached(&door_delay))
5681         BackToFront();
5682     }
5683   }
5684
5685   if (door_state & DOOR_ACTION_1)
5686     door1 = door_state & DOOR_ACTION_1;
5687   if (door_state & DOOR_ACTION_2)
5688     door2 = door_state & DOOR_ACTION_2;
5689
5690   // draw masked border over door area
5691   DrawMaskedBorder(REDRAW_DOOR_1);
5692   DrawMaskedBorder(REDRAW_DOOR_2);
5693
5694   ClearAutoRepeatKeyEvents();
5695
5696   return (door1 | door2);
5697 }
5698
5699 static boolean useSpecialEditorDoor(void)
5700 {
5701   int graphic = IMG_GLOBAL_BORDER_EDITOR;
5702   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5703
5704   // do not draw special editor door if editor border defined or redefined
5705   if (graphic_info[graphic].bitmap != NULL || redefined)
5706     return FALSE;
5707
5708   // do not draw special editor door if global border defined to be empty
5709   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5710     return FALSE;
5711
5712   // do not draw special editor door if viewport definitions do not match
5713   if (EX != VX ||
5714       EY >= VY ||
5715       EXSIZE != VXSIZE ||
5716       EY + EYSIZE != VY + VYSIZE)
5717     return FALSE;
5718
5719   return TRUE;
5720 }
5721
5722 void DrawSpecialEditorDoor(void)
5723 {
5724   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5725   int top_border_width = gfx1->width;
5726   int top_border_height = gfx1->height;
5727   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5728   int ex = EX - outer_border;
5729   int ey = EY - outer_border;
5730   int vy = VY - outer_border;
5731   int exsize = EXSIZE + 2 * outer_border;
5732
5733   if (!useSpecialEditorDoor())
5734     return;
5735
5736   // draw bigger level editor toolbox window
5737   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5738              top_border_width, top_border_height, ex, ey - top_border_height);
5739   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5740              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5741
5742   redraw_mask |= REDRAW_ALL;
5743 }
5744
5745 void UndrawSpecialEditorDoor(void)
5746 {
5747   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5748   int top_border_width = gfx1->width;
5749   int top_border_height = gfx1->height;
5750   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5751   int ex = EX - outer_border;
5752   int ey = EY - outer_border;
5753   int ey_top = ey - top_border_height;
5754   int exsize = EXSIZE + 2 * outer_border;
5755   int eysize = EYSIZE + 2 * outer_border;
5756
5757   if (!useSpecialEditorDoor())
5758     return;
5759
5760   // draw normal tape recorder window
5761   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5762   {
5763     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5764                ex, ey_top, top_border_width, top_border_height,
5765                ex, ey_top);
5766     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5767                ex, ey, exsize, eysize, ex, ey);
5768   }
5769   else
5770   {
5771     // if screen background is set to "[NONE]", clear editor toolbox window
5772     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5773     ClearRectangle(drawto, ex, ey, exsize, eysize);
5774   }
5775
5776   redraw_mask |= REDRAW_ALL;
5777 }
5778
5779
5780 // ---------- new tool button stuff -------------------------------------------
5781
5782 static struct
5783 {
5784   int graphic;
5785   struct TextPosInfo *pos;
5786   int gadget_id;
5787   boolean is_touch_button;
5788   char *infotext;
5789 } toolbutton_info[NUM_TOOL_BUTTONS] =
5790 {
5791   {
5792     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
5793     TOOL_CTRL_ID_YES, FALSE,            "yes"
5794   },
5795   {
5796     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5797     TOOL_CTRL_ID_NO, FALSE,             "no"
5798   },
5799   {
5800     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5801     TOOL_CTRL_ID_CONFIRM, FALSE,        "confirm"
5802   },
5803   {
5804     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5805     TOOL_CTRL_ID_PLAYER_1, FALSE,       "player 1"
5806   },
5807   {
5808     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5809     TOOL_CTRL_ID_PLAYER_2, FALSE,       "player 2"
5810   },
5811   {
5812     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5813     TOOL_CTRL_ID_PLAYER_3, FALSE,       "player 3"
5814   },
5815   {
5816     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5817     TOOL_CTRL_ID_PLAYER_4, FALSE,       "player 4"
5818   },
5819   {
5820     IMG_GFX_REQUEST_BUTTON_TOUCH_YES,   &request.button.touch_yes,
5821     TOOL_CTRL_ID_TOUCH_YES, TRUE,       "yes"
5822   },
5823   {
5824     IMG_GFX_REQUEST_BUTTON_TOUCH_NO,    &request.button.touch_no,
5825     TOOL_CTRL_ID_TOUCH_NO, TRUE,        "no"
5826   },
5827   {
5828     IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5829     TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE,   "confirm"
5830   }
5831 };
5832
5833 void CreateToolButtons(void)
5834 {
5835   int i;
5836
5837   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5838   {
5839     int graphic = toolbutton_info[i].graphic;
5840     struct GraphicInfo *gfx = &graphic_info[graphic];
5841     struct TextPosInfo *pos = toolbutton_info[i].pos;
5842     struct GadgetInfo *gi;
5843     Bitmap *deco_bitmap = None;
5844     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5845     unsigned int event_mask = GD_EVENT_RELEASED;
5846     boolean is_touch_button = toolbutton_info[i].is_touch_button;
5847     int base_x = (is_touch_button ? 0 : DX);
5848     int base_y = (is_touch_button ? 0 : DY);
5849     int gd_x = gfx->src_x;
5850     int gd_y = gfx->src_y;
5851     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5852     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5853     int x = pos->x;
5854     int y = pos->y;
5855     int id = i;
5856
5857     // do not use touch buttons if overlay touch buttons are disabled
5858     if (is_touch_button && !setup.touch.overlay_buttons)
5859       continue;
5860
5861     if (global.use_envelope_request && !is_touch_button)
5862     {
5863       setRequestPosition(&base_x, &base_y, TRUE);
5864
5865       // check if request buttons are outside of envelope and fix, if needed
5866       if (x < 0 || x + gfx->width  > request.width ||
5867           y < 0 || y + gfx->height > request.height)
5868       {
5869         if (id == TOOL_CTRL_ID_YES)
5870         {
5871           x = 0;
5872           y = request.height - 2 * request.border_size - gfx->height;
5873         }
5874         else if (id == TOOL_CTRL_ID_NO)
5875         {
5876           x = request.width  - 2 * request.border_size - gfx->width;
5877           y = request.height - 2 * request.border_size - gfx->height;
5878         }
5879         else if (id == TOOL_CTRL_ID_CONFIRM)
5880         {
5881           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5882           y = request.height - 2 * request.border_size - gfx->height;
5883         }
5884         else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5885         {
5886           int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5887
5888           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5889           y = request.height - 2 * request.border_size - gfx->height * 2;
5890
5891           x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5892           y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5893         }
5894       }
5895     }
5896
5897     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5898         pos->draw_player)
5899     {
5900       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5901
5902       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5903                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5904       deco_xpos = (gfx->width  - pos->size) / 2;
5905       deco_ypos = (gfx->height - pos->size) / 2;
5906     }
5907
5908     gi = CreateGadget(GDI_CUSTOM_ID, id,
5909                       GDI_IMAGE_ID, graphic,
5910                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5911                       GDI_X, base_x + x,
5912                       GDI_Y, base_y + y,
5913                       GDI_WIDTH, gfx->width,
5914                       GDI_HEIGHT, gfx->height,
5915                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5916                       GDI_STATE, GD_BUTTON_UNPRESSED,
5917                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5918                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5919                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5920                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5921                       GDI_DECORATION_SIZE, pos->size, pos->size,
5922                       GDI_DECORATION_SHIFTING, 1, 1,
5923                       GDI_DIRECT_DRAW, FALSE,
5924                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5925                       GDI_EVENT_MASK, event_mask,
5926                       GDI_CALLBACK_ACTION, HandleToolButtons,
5927                       GDI_END);
5928
5929     if (gi == NULL)
5930       Fail("cannot create gadget");
5931
5932     tool_gadget[id] = gi;
5933   }
5934 }
5935
5936 void FreeToolButtons(void)
5937 {
5938   int i;
5939
5940   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5941     FreeGadget(tool_gadget[i]);
5942 }
5943
5944 static void UnmapToolButtons(void)
5945 {
5946   int i;
5947
5948   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5949     UnmapGadget(tool_gadget[i]);
5950 }
5951
5952 static void HandleToolButtons(struct GadgetInfo *gi)
5953 {
5954   request_gadget_id = gi->custom_id;
5955 }
5956
5957 static struct Mapping_EM_to_RND_object
5958 {
5959   int element_em;
5960   boolean is_rnd_to_em_mapping;         // unique mapping EM <-> RND
5961   boolean is_backside;                  // backside of moving element
5962
5963   int element_rnd;
5964   int action;
5965   int direction;
5966 }
5967 em_object_mapping_list[GAME_TILE_MAX + 1] =
5968 {
5969   {
5970     Zborder,                            FALSE,  FALSE,
5971     EL_EMPTY,                           -1, -1
5972   },
5973   {
5974     Zplayer,                            FALSE,  FALSE,
5975     EL_EMPTY,                           -1, -1
5976   },
5977
5978   {
5979     Zbug,                               FALSE,  FALSE,
5980     EL_EMPTY,                           -1, -1
5981   },
5982   {
5983     Ztank,                              FALSE,  FALSE,
5984     EL_EMPTY,                           -1, -1
5985   },
5986   {
5987     Zeater,                             FALSE,  FALSE,
5988     EL_EMPTY,                           -1, -1
5989   },
5990   {
5991     Zdynamite,                          FALSE,  FALSE,
5992     EL_EMPTY,                           -1, -1
5993   },
5994   {
5995     Zboom,                              FALSE,  FALSE,
5996     EL_EMPTY,                           -1, -1
5997   },
5998
5999   {
6000     Xchain,                             FALSE,  FALSE,
6001     EL_DEFAULT,                         ACTION_EXPLODING, -1
6002   },
6003   {
6004     Xboom_bug,                          FALSE,  FALSE,
6005     EL_BUG,                             ACTION_EXPLODING, -1
6006   },
6007   {
6008     Xboom_tank,                         FALSE,  FALSE,
6009     EL_SPACESHIP,                       ACTION_EXPLODING, -1
6010   },
6011   {
6012     Xboom_android,                      FALSE,  FALSE,
6013     EL_EMC_ANDROID,                     ACTION_OTHER, -1
6014   },
6015   {
6016     Xboom_1,                            FALSE,  FALSE,
6017     EL_DEFAULT,                         ACTION_EXPLODING, -1
6018   },
6019   {
6020     Xboom_2,                            FALSE,  FALSE,
6021     EL_DEFAULT,                         ACTION_EXPLODING, -1
6022   },
6023
6024   {
6025     Xblank,                             TRUE,   FALSE,
6026     EL_EMPTY,                           -1, -1
6027   },
6028
6029   {
6030     Xsplash_e,                          FALSE,  FALSE,
6031     EL_ACID_SPLASH_RIGHT,               -1, -1
6032   },
6033   {
6034     Xsplash_w,                          FALSE,  FALSE,
6035     EL_ACID_SPLASH_LEFT,                -1, -1
6036   },
6037
6038   {
6039     Xplant,                             TRUE,   FALSE,
6040     EL_EMC_PLANT,                       -1, -1
6041   },
6042   {
6043     Yplant,                             FALSE,  FALSE,
6044     EL_EMC_PLANT,                       -1, -1
6045   },
6046
6047   {
6048     Xacid_1,                            TRUE,   FALSE,
6049     EL_ACID,                            -1, -1
6050   },
6051   {
6052     Xacid_2,                            FALSE,  FALSE,
6053     EL_ACID,                            -1, -1
6054   },
6055   {
6056     Xacid_3,                            FALSE,  FALSE,
6057     EL_ACID,                            -1, -1
6058   },
6059   {
6060     Xacid_4,                            FALSE,  FALSE,
6061     EL_ACID,                            -1, -1
6062   },
6063   {
6064     Xacid_5,                            FALSE,  FALSE,
6065     EL_ACID,                            -1, -1
6066   },
6067   {
6068     Xacid_6,                            FALSE,  FALSE,
6069     EL_ACID,                            -1, -1
6070   },
6071   {
6072     Xacid_7,                            FALSE,  FALSE,
6073     EL_ACID,                            -1, -1
6074   },
6075   {
6076     Xacid_8,                            FALSE,  FALSE,
6077     EL_ACID,                            -1, -1
6078   },
6079
6080   {
6081     Xfake_acid_1,                       TRUE,   FALSE,
6082     EL_EMC_FAKE_ACID,                   -1, -1
6083   },
6084   {
6085     Xfake_acid_2,                       FALSE,  FALSE,
6086     EL_EMC_FAKE_ACID,                   -1, -1
6087   },
6088   {
6089     Xfake_acid_3,                       FALSE,  FALSE,
6090     EL_EMC_FAKE_ACID,                   -1, -1
6091   },
6092   {
6093     Xfake_acid_4,                       FALSE,  FALSE,
6094     EL_EMC_FAKE_ACID,                   -1, -1
6095   },
6096   {
6097     Xfake_acid_5,                       FALSE,  FALSE,
6098     EL_EMC_FAKE_ACID,                   -1, -1
6099   },
6100   {
6101     Xfake_acid_6,                       FALSE,  FALSE,
6102     EL_EMC_FAKE_ACID,                   -1, -1
6103   },
6104   {
6105     Xfake_acid_7,                       FALSE,  FALSE,
6106     EL_EMC_FAKE_ACID,                   -1, -1
6107   },
6108   {
6109     Xfake_acid_8,                       FALSE,  FALSE,
6110     EL_EMC_FAKE_ACID,                   -1, -1
6111   },
6112
6113   {
6114     Xfake_acid_1_player,                FALSE,  FALSE,
6115     EL_EMC_FAKE_ACID,                   -1, -1
6116   },
6117   {
6118     Xfake_acid_2_player,                FALSE,  FALSE,
6119     EL_EMC_FAKE_ACID,                   -1, -1
6120   },
6121   {
6122     Xfake_acid_3_player,                FALSE,  FALSE,
6123     EL_EMC_FAKE_ACID,                   -1, -1
6124   },
6125   {
6126     Xfake_acid_4_player,                FALSE,  FALSE,
6127     EL_EMC_FAKE_ACID,                   -1, -1
6128   },
6129   {
6130     Xfake_acid_5_player,                FALSE,  FALSE,
6131     EL_EMC_FAKE_ACID,                   -1, -1
6132   },
6133   {
6134     Xfake_acid_6_player,                FALSE,  FALSE,
6135     EL_EMC_FAKE_ACID,                   -1, -1
6136   },
6137   {
6138     Xfake_acid_7_player,                FALSE,  FALSE,
6139     EL_EMC_FAKE_ACID,                   -1, -1
6140   },
6141   {
6142     Xfake_acid_8_player,                FALSE,  FALSE,
6143     EL_EMC_FAKE_ACID,                   -1, -1
6144   },
6145
6146   {
6147     Xgrass,                             TRUE,   FALSE,
6148     EL_EMC_GRASS,                       -1, -1
6149   },
6150   {
6151     Ygrass_nB,                          FALSE,  FALSE,
6152     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
6153   },
6154   {
6155     Ygrass_eB,                          FALSE,  FALSE,
6156     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
6157   },
6158   {
6159     Ygrass_sB,                          FALSE,  FALSE,
6160     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
6161   },
6162   {
6163     Ygrass_wB,                          FALSE,  FALSE,
6164     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
6165   },
6166
6167   {
6168     Xdirt,                              TRUE,   FALSE,
6169     EL_SAND,                            -1, -1
6170   },
6171   {
6172     Ydirt_nB,                           FALSE,  FALSE,
6173     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
6174   },
6175   {
6176     Ydirt_eB,                           FALSE,  FALSE,
6177     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
6178   },
6179   {
6180     Ydirt_sB,                           FALSE,  FALSE,
6181     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
6182   },
6183   {
6184     Ydirt_wB,                           FALSE,  FALSE,
6185     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
6186   },
6187
6188   {
6189     Xandroid,                           TRUE,   FALSE,
6190     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
6191   },
6192   {
6193     Xandroid_1_n,                       FALSE,  FALSE,
6194     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6195   },
6196   {
6197     Xandroid_2_n,                       FALSE,  FALSE,
6198     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6199   },
6200   {
6201     Xandroid_1_e,                       FALSE,  FALSE,
6202     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6203   },
6204   {
6205     Xandroid_2_e,                       FALSE,  FALSE,
6206     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6207   },
6208   {
6209     Xandroid_1_w,                       FALSE,  FALSE,
6210     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6211   },
6212   {
6213     Xandroid_2_w,                       FALSE,  FALSE,
6214     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6215   },
6216   {
6217     Xandroid_1_s,                       FALSE,  FALSE,
6218     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6219   },
6220   {
6221     Xandroid_2_s,                       FALSE,  FALSE,
6222     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6223   },
6224   {
6225     Yandroid_n,                         FALSE,  FALSE,
6226     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6227   },
6228   {
6229     Yandroid_nB,                        FALSE,  TRUE,
6230     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6231   },
6232   {
6233     Yandroid_ne,                        FALSE,  FALSE,
6234     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
6235   },
6236   {
6237     Yandroid_neB,                       FALSE,  TRUE,
6238     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
6239   },
6240   {
6241     Yandroid_e,                         FALSE,  FALSE,
6242     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6243   },
6244   {
6245     Yandroid_eB,                        FALSE,  TRUE,
6246     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6247   },
6248   {
6249     Yandroid_se,                        FALSE,  FALSE,
6250     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
6251   },
6252   {
6253     Yandroid_seB,                       FALSE,  TRUE,
6254     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6255   },
6256   {
6257     Yandroid_s,                         FALSE,  FALSE,
6258     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6259   },
6260   {
6261     Yandroid_sB,                        FALSE,  TRUE,
6262     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6263   },
6264   {
6265     Yandroid_sw,                        FALSE,  FALSE,
6266     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
6267   },
6268   {
6269     Yandroid_swB,                       FALSE,  TRUE,
6270     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
6271   },
6272   {
6273     Yandroid_w,                         FALSE,  FALSE,
6274     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6275   },
6276   {
6277     Yandroid_wB,                        FALSE,  TRUE,
6278     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6279   },
6280   {
6281     Yandroid_nw,                        FALSE,  FALSE,
6282     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
6283   },
6284   {
6285     Yandroid_nwB,                       FALSE,  TRUE,
6286     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
6287   },
6288
6289   {
6290     Xeater_n,                           TRUE,   FALSE,
6291     EL_YAMYAM_UP,                       -1, -1
6292   },
6293   {
6294     Xeater_e,                           TRUE,   FALSE,
6295     EL_YAMYAM_RIGHT,                    -1, -1
6296   },
6297   {
6298     Xeater_w,                           TRUE,   FALSE,
6299     EL_YAMYAM_LEFT,                     -1, -1
6300   },
6301   {
6302     Xeater_s,                           TRUE,   FALSE,
6303     EL_YAMYAM_DOWN,                     -1, -1
6304   },
6305   {
6306     Yeater_n,                           FALSE,  FALSE,
6307     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6308   },
6309   {
6310     Yeater_nB,                          FALSE,  TRUE,
6311     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6312   },
6313   {
6314     Yeater_e,                           FALSE,  FALSE,
6315     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6316   },
6317   {
6318     Yeater_eB,                          FALSE,  TRUE,
6319     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6320   },
6321   {
6322     Yeater_s,                           FALSE,  FALSE,
6323     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6324   },
6325   {
6326     Yeater_sB,                          FALSE,  TRUE,
6327     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6328   },
6329   {
6330     Yeater_w,                           FALSE,  FALSE,
6331     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6332   },
6333   {
6334     Yeater_wB,                          FALSE,  TRUE,
6335     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6336   },
6337   {
6338     Yeater_stone,                       FALSE,  FALSE,
6339     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
6340   },
6341   {
6342     Yeater_spring,                      FALSE,  FALSE,
6343     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
6344   },
6345
6346   {
6347     Xalien,                             TRUE,   FALSE,
6348     EL_ROBOT,                           -1, -1
6349   },
6350   {
6351     Xalien_pause,                       FALSE,  FALSE,
6352     EL_ROBOT,                           -1, -1
6353   },
6354   {
6355     Yalien_n,                           FALSE,  FALSE,
6356     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6357   },
6358   {
6359     Yalien_nB,                          FALSE,  TRUE,
6360     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6361   },
6362   {
6363     Yalien_e,                           FALSE,  FALSE,
6364     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6365   },
6366   {
6367     Yalien_eB,                          FALSE,  TRUE,
6368     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6369   },
6370   {
6371     Yalien_s,                           FALSE,  FALSE,
6372     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6373   },
6374   {
6375     Yalien_sB,                          FALSE,  TRUE,
6376     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6377   },
6378   {
6379     Yalien_w,                           FALSE,  FALSE,
6380     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6381   },
6382   {
6383     Yalien_wB,                          FALSE,  TRUE,
6384     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6385   },
6386   {
6387     Yalien_stone,                       FALSE,  FALSE,
6388     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
6389   },
6390   {
6391     Yalien_spring,                      FALSE,  FALSE,
6392     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
6393   },
6394
6395   {
6396     Xbug_1_n,                           TRUE,   FALSE,
6397     EL_BUG_UP,                          -1, -1
6398   },
6399   {
6400     Xbug_1_e,                           TRUE,   FALSE,
6401     EL_BUG_RIGHT,                       -1, -1
6402   },
6403   {
6404     Xbug_1_s,                           TRUE,   FALSE,
6405     EL_BUG_DOWN,                        -1, -1
6406   },
6407   {
6408     Xbug_1_w,                           TRUE,   FALSE,
6409     EL_BUG_LEFT,                        -1, -1
6410   },
6411   {
6412     Xbug_2_n,                           FALSE,  FALSE,
6413     EL_BUG_UP,                          -1, -1
6414   },
6415   {
6416     Xbug_2_e,                           FALSE,  FALSE,
6417     EL_BUG_RIGHT,                       -1, -1
6418   },
6419   {
6420     Xbug_2_s,                           FALSE,  FALSE,
6421     EL_BUG_DOWN,                        -1, -1
6422   },
6423   {
6424     Xbug_2_w,                           FALSE,  FALSE,
6425     EL_BUG_LEFT,                        -1, -1
6426   },
6427   {
6428     Ybug_n,                             FALSE,  FALSE,
6429     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6430   },
6431   {
6432     Ybug_nB,                            FALSE,  TRUE,
6433     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6434   },
6435   {
6436     Ybug_e,                             FALSE,  FALSE,
6437     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6438   },
6439   {
6440     Ybug_eB,                            FALSE,  TRUE,
6441     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6442   },
6443   {
6444     Ybug_s,                             FALSE,  FALSE,
6445     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6446   },
6447   {
6448     Ybug_sB,                            FALSE,  TRUE,
6449     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6450   },
6451   {
6452     Ybug_w,                             FALSE,  FALSE,
6453     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6454   },
6455   {
6456     Ybug_wB,                            FALSE,  TRUE,
6457     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6458   },
6459   {
6460     Ybug_w_n,                           FALSE,  FALSE,
6461     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6462   },
6463   {
6464     Ybug_n_e,                           FALSE,  FALSE,
6465     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6466   },
6467   {
6468     Ybug_e_s,                           FALSE,  FALSE,
6469     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6470   },
6471   {
6472     Ybug_s_w,                           FALSE,  FALSE,
6473     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6474   },
6475   {
6476     Ybug_e_n,                           FALSE,  FALSE,
6477     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6478   },
6479   {
6480     Ybug_s_e,                           FALSE,  FALSE,
6481     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6482   },
6483   {
6484     Ybug_w_s,                           FALSE,  FALSE,
6485     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6486   },
6487   {
6488     Ybug_n_w,                           FALSE,  FALSE,
6489     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6490   },
6491   {
6492     Ybug_stone,                         FALSE,  FALSE,
6493     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
6494   },
6495   {
6496     Ybug_spring,                        FALSE,  FALSE,
6497     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
6498   },
6499
6500   {
6501     Xtank_1_n,                          TRUE,   FALSE,
6502     EL_SPACESHIP_UP,                    -1, -1
6503   },
6504   {
6505     Xtank_1_e,                          TRUE,   FALSE,
6506     EL_SPACESHIP_RIGHT,                 -1, -1
6507   },
6508   {
6509     Xtank_1_s,                          TRUE,   FALSE,
6510     EL_SPACESHIP_DOWN,                  -1, -1
6511   },
6512   {
6513     Xtank_1_w,                          TRUE,   FALSE,
6514     EL_SPACESHIP_LEFT,                  -1, -1
6515   },
6516   {
6517     Xtank_2_n,                          FALSE,  FALSE,
6518     EL_SPACESHIP_UP,                    -1, -1
6519   },
6520   {
6521     Xtank_2_e,                          FALSE,  FALSE,
6522     EL_SPACESHIP_RIGHT,                 -1, -1
6523   },
6524   {
6525     Xtank_2_s,                          FALSE,  FALSE,
6526     EL_SPACESHIP_DOWN,                  -1, -1
6527   },
6528   {
6529     Xtank_2_w,                          FALSE,  FALSE,
6530     EL_SPACESHIP_LEFT,                  -1, -1
6531   },
6532   {
6533     Ytank_n,                            FALSE,  FALSE,
6534     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6535   },
6536   {
6537     Ytank_nB,                           FALSE,  TRUE,
6538     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6539   },
6540   {
6541     Ytank_e,                            FALSE,  FALSE,
6542     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6543   },
6544   {
6545     Ytank_eB,                           FALSE,  TRUE,
6546     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6547   },
6548   {
6549     Ytank_s,                            FALSE,  FALSE,
6550     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6551   },
6552   {
6553     Ytank_sB,                           FALSE,  TRUE,
6554     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6555   },
6556   {
6557     Ytank_w,                            FALSE,  FALSE,
6558     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6559   },
6560   {
6561     Ytank_wB,                           FALSE,  TRUE,
6562     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6563   },
6564   {
6565     Ytank_w_n,                          FALSE,  FALSE,
6566     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6567   },
6568   {
6569     Ytank_n_e,                          FALSE,  FALSE,
6570     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6571   },
6572   {
6573     Ytank_e_s,                          FALSE,  FALSE,
6574     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6575   },
6576   {
6577     Ytank_s_w,                          FALSE,  FALSE,
6578     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6579   },
6580   {
6581     Ytank_e_n,                          FALSE,  FALSE,
6582     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6583   },
6584   {
6585     Ytank_s_e,                          FALSE,  FALSE,
6586     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6587   },
6588   {
6589     Ytank_w_s,                          FALSE,  FALSE,
6590     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6591   },
6592   {
6593     Ytank_n_w,                          FALSE,  FALSE,
6594     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6595   },
6596   {
6597     Ytank_stone,                        FALSE,  FALSE,
6598     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
6599   },
6600   {
6601     Ytank_spring,                       FALSE,  FALSE,
6602     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
6603   },
6604
6605   {
6606     Xemerald,                           TRUE,   FALSE,
6607     EL_EMERALD,                         -1, -1
6608   },
6609   {
6610     Xemerald_pause,                     FALSE,  FALSE,
6611     EL_EMERALD,                         -1, -1
6612   },
6613   {
6614     Xemerald_fall,                      FALSE,  FALSE,
6615     EL_EMERALD,                         -1, -1
6616   },
6617   {
6618     Xemerald_shine,                     FALSE,  FALSE,
6619     EL_EMERALD,                         ACTION_TWINKLING, -1
6620   },
6621   {
6622     Yemerald_s,                         FALSE,  FALSE,
6623     EL_EMERALD,                         ACTION_FALLING, -1
6624   },
6625   {
6626     Yemerald_sB,                        FALSE,  TRUE,
6627     EL_EMERALD,                         ACTION_FALLING, -1
6628   },
6629   {
6630     Yemerald_e,                         FALSE,  FALSE,
6631     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6632   },
6633   {
6634     Yemerald_eB,                        FALSE,  TRUE,
6635     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6636   },
6637   {
6638     Yemerald_w,                         FALSE,  FALSE,
6639     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6640   },
6641   {
6642     Yemerald_wB,                        FALSE,  TRUE,
6643     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6644   },
6645   {
6646     Yemerald_blank,                     FALSE,  FALSE,
6647     EL_EMERALD,                         ACTION_COLLECTING, -1
6648   },
6649
6650   {
6651     Xdiamond,                           TRUE,   FALSE,
6652     EL_DIAMOND,                         -1, -1
6653   },
6654   {
6655     Xdiamond_pause,                     FALSE,  FALSE,
6656     EL_DIAMOND,                         -1, -1
6657   },
6658   {
6659     Xdiamond_fall,                      FALSE,  FALSE,
6660     EL_DIAMOND,                         -1, -1
6661   },
6662   {
6663     Xdiamond_shine,                     FALSE,  FALSE,
6664     EL_DIAMOND,                         ACTION_TWINKLING, -1
6665   },
6666   {
6667     Ydiamond_s,                         FALSE,  FALSE,
6668     EL_DIAMOND,                         ACTION_FALLING, -1
6669   },
6670   {
6671     Ydiamond_sB,                        FALSE,  TRUE,
6672     EL_DIAMOND,                         ACTION_FALLING, -1
6673   },
6674   {
6675     Ydiamond_e,                         FALSE,  FALSE,
6676     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6677   },
6678   {
6679     Ydiamond_eB,                        FALSE,  TRUE,
6680     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6681   },
6682   {
6683     Ydiamond_w,                         FALSE,  FALSE,
6684     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6685   },
6686   {
6687     Ydiamond_wB,                        FALSE,  TRUE,
6688     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6689   },
6690   {
6691     Ydiamond_blank,                     FALSE,  FALSE,
6692     EL_DIAMOND,                         ACTION_COLLECTING, -1
6693   },
6694   {
6695     Ydiamond_stone,                     FALSE,  FALSE,
6696     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
6697   },
6698
6699   {
6700     Xstone,                             TRUE,   FALSE,
6701     EL_ROCK,                            -1, -1
6702   },
6703   {
6704     Xstone_pause,                       FALSE,  FALSE,
6705     EL_ROCK,                            -1, -1
6706   },
6707   {
6708     Xstone_fall,                        FALSE,  FALSE,
6709     EL_ROCK,                            -1, -1
6710   },
6711   {
6712     Ystone_s,                           FALSE,  FALSE,
6713     EL_ROCK,                            ACTION_FALLING, -1
6714   },
6715   {
6716     Ystone_sB,                          FALSE,  TRUE,
6717     EL_ROCK,                            ACTION_FALLING, -1
6718   },
6719   {
6720     Ystone_e,                           FALSE,  FALSE,
6721     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6722   },
6723   {
6724     Ystone_eB,                          FALSE,  TRUE,
6725     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6726   },
6727   {
6728     Ystone_w,                           FALSE,  FALSE,
6729     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6730   },
6731   {
6732     Ystone_wB,                          FALSE,  TRUE,
6733     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6734   },
6735
6736   {
6737     Xbomb,                              TRUE,   FALSE,
6738     EL_BOMB,                            -1, -1
6739   },
6740   {
6741     Xbomb_pause,                        FALSE,  FALSE,
6742     EL_BOMB,                            -1, -1
6743   },
6744   {
6745     Xbomb_fall,                         FALSE,  FALSE,
6746     EL_BOMB,                            -1, -1
6747   },
6748   {
6749     Ybomb_s,                            FALSE,  FALSE,
6750     EL_BOMB,                            ACTION_FALLING, -1
6751   },
6752   {
6753     Ybomb_sB,                           FALSE,  TRUE,
6754     EL_BOMB,                            ACTION_FALLING, -1
6755   },
6756   {
6757     Ybomb_e,                            FALSE,  FALSE,
6758     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6759   },
6760   {
6761     Ybomb_eB,                           FALSE,  TRUE,
6762     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6763   },
6764   {
6765     Ybomb_w,                            FALSE,  FALSE,
6766     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6767   },
6768   {
6769     Ybomb_wB,                           FALSE,  TRUE,
6770     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6771   },
6772   {
6773     Ybomb_blank,                        FALSE,  FALSE,
6774     EL_BOMB,                            ACTION_ACTIVATING, -1
6775   },
6776
6777   {
6778     Xnut,                               TRUE,   FALSE,
6779     EL_NUT,                             -1, -1
6780   },
6781   {
6782     Xnut_pause,                         FALSE,  FALSE,
6783     EL_NUT,                             -1, -1
6784   },
6785   {
6786     Xnut_fall,                          FALSE,  FALSE,
6787     EL_NUT,                             -1, -1
6788   },
6789   {
6790     Ynut_s,                             FALSE,  FALSE,
6791     EL_NUT,                             ACTION_FALLING, -1
6792   },
6793   {
6794     Ynut_sB,                            FALSE,  TRUE,
6795     EL_NUT,                             ACTION_FALLING, -1
6796   },
6797   {
6798     Ynut_e,                             FALSE,  FALSE,
6799     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6800   },
6801   {
6802     Ynut_eB,                            FALSE,  TRUE,
6803     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6804   },
6805   {
6806     Ynut_w,                             FALSE,  FALSE,
6807     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6808   },
6809   {
6810     Ynut_wB,                            FALSE,  TRUE,
6811     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6812   },
6813   {
6814     Ynut_stone,                         FALSE,  FALSE,
6815     EL_NUT,                             ACTION_BREAKING, -1
6816   },
6817
6818   {
6819     Xspring,                            TRUE,   FALSE,
6820     EL_SPRING,                          -1, -1
6821   },
6822   {
6823     Xspring_pause,                      FALSE,  FALSE,
6824     EL_SPRING,                          -1, -1
6825   },
6826   {
6827     Xspring_e,                          TRUE,   FALSE,
6828     EL_SPRING_RIGHT,                    -1, -1
6829   },
6830   {
6831     Xspring_w,                          TRUE,   FALSE,
6832     EL_SPRING_LEFT,                     -1, -1
6833   },
6834   {
6835     Xspring_fall,                       FALSE,  FALSE,
6836     EL_SPRING,                          -1, -1
6837   },
6838   {
6839     Yspring_s,                          FALSE,  FALSE,
6840     EL_SPRING,                          ACTION_FALLING, -1
6841   },
6842   {
6843     Yspring_sB,                         FALSE,  TRUE,
6844     EL_SPRING,                          ACTION_FALLING, -1
6845   },
6846   {
6847     Yspring_e,                          FALSE,  FALSE,
6848     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6849   },
6850   {
6851     Yspring_eB,                         FALSE,  TRUE,
6852     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6853   },
6854   {
6855     Yspring_w,                          FALSE,  FALSE,
6856     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6857   },
6858   {
6859     Yspring_wB,                         FALSE,  TRUE,
6860     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6861   },
6862   {
6863     Yspring_alien_e,                    FALSE,  FALSE,
6864     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6865   },
6866   {
6867     Yspring_alien_eB,                   FALSE,  TRUE,
6868     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6869   },
6870   {
6871     Yspring_alien_w,                    FALSE,  FALSE,
6872     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6873   },
6874   {
6875     Yspring_alien_wB,                   FALSE,  TRUE,
6876     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6877   },
6878
6879   {
6880     Xpush_emerald_e,                    FALSE,  FALSE,
6881     EL_EMERALD,                         -1, MV_BIT_RIGHT
6882   },
6883   {
6884     Xpush_emerald_w,                    FALSE,  FALSE,
6885     EL_EMERALD,                         -1, MV_BIT_LEFT
6886   },
6887   {
6888     Xpush_diamond_e,                    FALSE,  FALSE,
6889     EL_DIAMOND,                         -1, MV_BIT_RIGHT
6890   },
6891   {
6892     Xpush_diamond_w,                    FALSE,  FALSE,
6893     EL_DIAMOND,                         -1, MV_BIT_LEFT
6894   },
6895   {
6896     Xpush_stone_e,                      FALSE,  FALSE,
6897     EL_ROCK,                            -1, MV_BIT_RIGHT
6898   },
6899   {
6900     Xpush_stone_w,                      FALSE,  FALSE,
6901     EL_ROCK,                            -1, MV_BIT_LEFT
6902   },
6903   {
6904     Xpush_bomb_e,                       FALSE,  FALSE,
6905     EL_BOMB,                            -1, MV_BIT_RIGHT
6906   },
6907   {
6908     Xpush_bomb_w,                       FALSE,  FALSE,
6909     EL_BOMB,                            -1, MV_BIT_LEFT
6910   },
6911   {
6912     Xpush_nut_e,                        FALSE,  FALSE,
6913     EL_NUT,                             -1, MV_BIT_RIGHT
6914   },
6915   {
6916     Xpush_nut_w,                        FALSE,  FALSE,
6917     EL_NUT,                             -1, MV_BIT_LEFT
6918   },
6919   {
6920     Xpush_spring_e,                     FALSE,  FALSE,
6921     EL_SPRING_RIGHT,                    -1, MV_BIT_RIGHT
6922   },
6923   {
6924     Xpush_spring_w,                     FALSE,  FALSE,
6925     EL_SPRING_LEFT,                     -1, MV_BIT_LEFT
6926   },
6927
6928   {
6929     Xdynamite,                          TRUE,   FALSE,
6930     EL_EM_DYNAMITE,                     -1, -1
6931   },
6932   {
6933     Ydynamite_blank,                    FALSE,  FALSE,
6934     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6935   },
6936   {
6937     Xdynamite_1,                        TRUE,   FALSE,
6938     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6939   },
6940   {
6941     Xdynamite_2,                        FALSE,  FALSE,
6942     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6943   },
6944   {
6945     Xdynamite_3,                        FALSE,  FALSE,
6946     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6947   },
6948   {
6949     Xdynamite_4,                        FALSE,  FALSE,
6950     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6951   },
6952
6953   {
6954     Xkey_1,                             TRUE,   FALSE,
6955     EL_EM_KEY_1,                        -1, -1
6956   },
6957   {
6958     Xkey_2,                             TRUE,   FALSE,
6959     EL_EM_KEY_2,                        -1, -1
6960   },
6961   {
6962     Xkey_3,                             TRUE,   FALSE,
6963     EL_EM_KEY_3,                        -1, -1
6964   },
6965   {
6966     Xkey_4,                             TRUE,   FALSE,
6967     EL_EM_KEY_4,                        -1, -1
6968   },
6969   {
6970     Xkey_5,                             TRUE,   FALSE,
6971     EL_EMC_KEY_5,                       -1, -1
6972   },
6973   {
6974     Xkey_6,                             TRUE,   FALSE,
6975     EL_EMC_KEY_6,                       -1, -1
6976   },
6977   {
6978     Xkey_7,                             TRUE,   FALSE,
6979     EL_EMC_KEY_7,                       -1, -1
6980   },
6981   {
6982     Xkey_8,                             TRUE,   FALSE,
6983     EL_EMC_KEY_8,                       -1, -1
6984   },
6985
6986   {
6987     Xdoor_1,                            TRUE,   FALSE,
6988     EL_EM_GATE_1,                       -1, -1
6989   },
6990   {
6991     Xdoor_2,                            TRUE,   FALSE,
6992     EL_EM_GATE_2,                       -1, -1
6993   },
6994   {
6995     Xdoor_3,                            TRUE,   FALSE,
6996     EL_EM_GATE_3,                       -1, -1
6997   },
6998   {
6999     Xdoor_4,                            TRUE,   FALSE,
7000     EL_EM_GATE_4,                       -1, -1
7001   },
7002   {
7003     Xdoor_5,                            TRUE,   FALSE,
7004     EL_EMC_GATE_5,                      -1, -1
7005   },
7006   {
7007     Xdoor_6,                            TRUE,   FALSE,
7008     EL_EMC_GATE_6,                      -1, -1
7009   },
7010   {
7011     Xdoor_7,                            TRUE,   FALSE,
7012     EL_EMC_GATE_7,                      -1, -1
7013   },
7014   {
7015     Xdoor_8,                            TRUE,   FALSE,
7016     EL_EMC_GATE_8,                      -1, -1
7017   },
7018
7019   {
7020     Xfake_door_1,                       TRUE,   FALSE,
7021     EL_EM_GATE_1_GRAY,                  -1, -1
7022   },
7023   {
7024     Xfake_door_2,                       TRUE,   FALSE,
7025     EL_EM_GATE_2_GRAY,                  -1, -1
7026   },
7027   {
7028     Xfake_door_3,                       TRUE,   FALSE,
7029     EL_EM_GATE_3_GRAY,                  -1, -1
7030   },
7031   {
7032     Xfake_door_4,                       TRUE,   FALSE,
7033     EL_EM_GATE_4_GRAY,                  -1, -1
7034   },
7035   {
7036     Xfake_door_5,                       TRUE,   FALSE,
7037     EL_EMC_GATE_5_GRAY,                 -1, -1
7038   },
7039   {
7040     Xfake_door_6,                       TRUE,   FALSE,
7041     EL_EMC_GATE_6_GRAY,                 -1, -1
7042   },
7043   {
7044     Xfake_door_7,                       TRUE,   FALSE,
7045     EL_EMC_GATE_7_GRAY,                 -1, -1
7046   },
7047   {
7048     Xfake_door_8,                       TRUE,   FALSE,
7049     EL_EMC_GATE_8_GRAY,                 -1, -1
7050   },
7051
7052   {
7053     Xballoon,                           TRUE,   FALSE,
7054     EL_BALLOON,                         -1, -1
7055   },
7056   {
7057     Yballoon_n,                         FALSE,  FALSE,
7058     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
7059   },
7060   {
7061     Yballoon_nB,                        FALSE,  TRUE,
7062     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
7063   },
7064   {
7065     Yballoon_e,                         FALSE,  FALSE,
7066     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
7067   },
7068   {
7069     Yballoon_eB,                        FALSE,  TRUE,
7070     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
7071   },
7072   {
7073     Yballoon_s,                         FALSE,  FALSE,
7074     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
7075   },
7076   {
7077     Yballoon_sB,                        FALSE,  TRUE,
7078     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
7079   },
7080   {
7081     Yballoon_w,                         FALSE,  FALSE,
7082     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
7083   },
7084   {
7085     Yballoon_wB,                        FALSE,  TRUE,
7086     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
7087   },
7088
7089   {
7090     Xball_1,                            TRUE,   FALSE,
7091     EL_EMC_MAGIC_BALL,                  -1, -1
7092   },
7093   {
7094     Yball_1,                            FALSE,  FALSE,
7095     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7096   },
7097   {
7098     Xball_2,                            FALSE,  FALSE,
7099     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7100   },
7101   {
7102     Yball_2,                            FALSE,  FALSE,
7103     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7104   },
7105   {
7106     Yball_blank,                        FALSE,  FALSE,
7107     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
7108   },
7109
7110   {
7111     Xamoeba_1,                          TRUE,   FALSE,
7112     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7113   },
7114   {
7115     Xamoeba_2,                          FALSE,  FALSE,
7116     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7117   },
7118   {
7119     Xamoeba_3,                          FALSE,  FALSE,
7120     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7121   },
7122   {
7123     Xamoeba_4,                          FALSE,  FALSE,
7124     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7125   },
7126   {
7127     Xamoeba_5,                          TRUE,   FALSE,
7128     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7129   },
7130   {
7131     Xamoeba_6,                          FALSE,  FALSE,
7132     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7133   },
7134   {
7135     Xamoeba_7,                          FALSE,  FALSE,
7136     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7137   },
7138   {
7139     Xamoeba_8,                          FALSE,  FALSE,
7140     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7141   },
7142
7143   {
7144     Xdrip,                              TRUE,   FALSE,
7145     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
7146   },
7147   {
7148     Xdrip_fall,                         FALSE,  FALSE,
7149     EL_AMOEBA_DROP,                     -1, -1
7150   },
7151   {
7152     Xdrip_stretch,                      FALSE,  FALSE,
7153     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7154   },
7155   {
7156     Xdrip_stretchB,                     FALSE,  TRUE,
7157     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7158   },
7159   {
7160     Ydrip_1_s,                          FALSE,  FALSE,
7161     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7162   },
7163   {
7164     Ydrip_1_sB,                         FALSE,  TRUE,
7165     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7166   },
7167   {
7168     Ydrip_2_s,                          FALSE,  FALSE,
7169     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7170   },
7171   {
7172     Ydrip_2_sB,                         FALSE,  TRUE,
7173     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7174   },
7175
7176   {
7177     Xwonderwall,                        TRUE,   FALSE,
7178     EL_MAGIC_WALL,                      -1, -1
7179   },
7180   {
7181     Ywonderwall,                        FALSE,  FALSE,
7182     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
7183   },
7184
7185   {
7186     Xwheel,                             TRUE,   FALSE,
7187     EL_ROBOT_WHEEL,                     -1, -1
7188   },
7189   {
7190     Ywheel,                             FALSE,  FALSE,
7191     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
7192   },
7193
7194   {
7195     Xswitch,                            TRUE,   FALSE,
7196     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
7197   },
7198   {
7199     Yswitch,                            FALSE,  FALSE,
7200     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
7201   },
7202
7203   {
7204     Xbumper,                            TRUE,   FALSE,
7205     EL_EMC_SPRING_BUMPER,               -1, -1
7206   },
7207   {
7208     Ybumper,                            FALSE,  FALSE,
7209     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
7210   },
7211
7212   {
7213     Xacid_nw,                           TRUE,   FALSE,
7214     EL_ACID_POOL_TOPLEFT,               -1, -1
7215   },
7216   {
7217     Xacid_ne,                           TRUE,   FALSE,
7218     EL_ACID_POOL_TOPRIGHT,              -1, -1
7219   },
7220   {
7221     Xacid_sw,                           TRUE,   FALSE,
7222     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
7223   },
7224   {
7225     Xacid_s,                            TRUE,   FALSE,
7226     EL_ACID_POOL_BOTTOM,                -1, -1
7227   },
7228   {
7229     Xacid_se,                           TRUE,   FALSE,
7230     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
7231   },
7232
7233   {
7234     Xfake_blank,                        TRUE,   FALSE,
7235     EL_INVISIBLE_WALL,                  -1, -1
7236   },
7237   {
7238     Yfake_blank,                        FALSE,  FALSE,
7239     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
7240   },
7241
7242   {
7243     Xfake_grass,                        TRUE,   FALSE,
7244     EL_EMC_FAKE_GRASS,                  -1, -1
7245   },
7246   {
7247     Yfake_grass,                        FALSE,  FALSE,
7248     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
7249   },
7250
7251   {
7252     Xfake_amoeba,                       TRUE,   FALSE,
7253     EL_EMC_DRIPPER,                     -1, -1
7254   },
7255   {
7256     Yfake_amoeba,                       FALSE,  FALSE,
7257     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
7258   },
7259
7260   {
7261     Xlenses,                            TRUE,   FALSE,
7262     EL_EMC_LENSES,                      -1, -1
7263   },
7264
7265   {
7266     Xmagnify,                           TRUE,   FALSE,
7267     EL_EMC_MAGNIFIER,                   -1, -1
7268   },
7269
7270   {
7271     Xsand,                              TRUE,   FALSE,
7272     EL_QUICKSAND_EMPTY,                 -1, -1
7273   },
7274   {
7275     Xsand_stone,                        TRUE,   FALSE,
7276     EL_QUICKSAND_FULL,                  -1, -1
7277   },
7278   {
7279     Xsand_stonein_1,                    FALSE,  TRUE,
7280     EL_ROCK,                            ACTION_FILLING, -1
7281   },
7282   {
7283     Xsand_stonein_2,                    FALSE,  TRUE,
7284     EL_ROCK,                            ACTION_FILLING, -1
7285   },
7286   {
7287     Xsand_stonein_3,                    FALSE,  TRUE,
7288     EL_ROCK,                            ACTION_FILLING, -1
7289   },
7290   {
7291     Xsand_stonein_4,                    FALSE,  TRUE,
7292     EL_ROCK,                            ACTION_FILLING, -1
7293   },
7294   {
7295     Xsand_sandstone_1,                  FALSE,  FALSE,
7296     EL_QUICKSAND_FILLING,               -1, -1
7297   },
7298   {
7299     Xsand_sandstone_2,                  FALSE,  FALSE,
7300     EL_QUICKSAND_FILLING,               -1, -1
7301   },
7302   {
7303     Xsand_sandstone_3,                  FALSE,  FALSE,
7304     EL_QUICKSAND_FILLING,               -1, -1
7305   },
7306   {
7307     Xsand_sandstone_4,                  FALSE,  FALSE,
7308     EL_QUICKSAND_FILLING,               -1, -1
7309   },
7310   {
7311     Xsand_stonesand_1,                  FALSE,  FALSE,
7312     EL_QUICKSAND_EMPTYING,              -1, -1
7313   },
7314   {
7315     Xsand_stonesand_2,                  FALSE,  FALSE,
7316     EL_QUICKSAND_EMPTYING,              -1, -1
7317   },
7318   {
7319     Xsand_stonesand_3,                  FALSE,  FALSE,
7320     EL_QUICKSAND_EMPTYING,              -1, -1
7321   },
7322   {
7323     Xsand_stonesand_4,                  FALSE,  FALSE,
7324     EL_QUICKSAND_EMPTYING,              -1, -1
7325   },
7326   {
7327     Xsand_stoneout_1,                   FALSE,  FALSE,
7328     EL_ROCK,                            ACTION_EMPTYING, -1
7329   },
7330   {
7331     Xsand_stoneout_2,                   FALSE,  FALSE,
7332     EL_ROCK,                            ACTION_EMPTYING, -1
7333   },
7334   {
7335     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
7336     EL_QUICKSAND_EMPTYING,              -1, -1
7337   },
7338   {
7339     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
7340     EL_QUICKSAND_EMPTYING,              -1, -1
7341   },
7342
7343   {
7344     Xslide_ns,                          TRUE,   FALSE,
7345     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
7346   },
7347   {
7348     Yslide_ns_blank,                    FALSE,  FALSE,
7349     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
7350   },
7351   {
7352     Xslide_ew,                          TRUE,   FALSE,
7353     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
7354   },
7355   {
7356     Yslide_ew_blank,                    FALSE,  FALSE,
7357     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
7358   },
7359
7360   {
7361     Xwind_n,                            TRUE,   FALSE,
7362     EL_BALLOON_SWITCH_UP,               -1, -1
7363   },
7364   {
7365     Xwind_e,                            TRUE,   FALSE,
7366     EL_BALLOON_SWITCH_RIGHT,            -1, -1
7367   },
7368   {
7369     Xwind_s,                            TRUE,   FALSE,
7370     EL_BALLOON_SWITCH_DOWN,             -1, -1
7371   },
7372   {
7373     Xwind_w,                            TRUE,   FALSE,
7374     EL_BALLOON_SWITCH_LEFT,             -1, -1
7375   },
7376   {
7377     Xwind_any,                          TRUE,   FALSE,
7378     EL_BALLOON_SWITCH_ANY,              -1, -1
7379   },
7380   {
7381     Xwind_stop,                         TRUE,   FALSE,
7382     EL_BALLOON_SWITCH_NONE,             -1, -1
7383   },
7384
7385   {
7386     Xexit,                              TRUE,   FALSE,
7387     EL_EM_EXIT_CLOSED,                  -1, -1
7388   },
7389   {
7390     Xexit_1,                            TRUE,   FALSE,
7391     EL_EM_EXIT_OPEN,                    -1, -1
7392   },
7393   {
7394     Xexit_2,                            FALSE,  FALSE,
7395     EL_EM_EXIT_OPEN,                    -1, -1
7396   },
7397   {
7398     Xexit_3,                            FALSE,  FALSE,
7399     EL_EM_EXIT_OPEN,                    -1, -1
7400   },
7401
7402   {
7403     Xpause,                             FALSE,  FALSE,
7404     EL_EMPTY,                           -1, -1
7405   },
7406
7407   {
7408     Xwall_1,                            TRUE,   FALSE,
7409     EL_WALL,                            -1, -1
7410   },
7411   {
7412     Xwall_2,                            TRUE,   FALSE,
7413     EL_EMC_WALL_14,                     -1, -1
7414   },
7415   {
7416     Xwall_3,                            TRUE,   FALSE,
7417     EL_EMC_WALL_15,                     -1, -1
7418   },
7419   {
7420     Xwall_4,                            TRUE,   FALSE,
7421     EL_EMC_WALL_16,                     -1, -1
7422   },
7423
7424   {
7425     Xroundwall_1,                       TRUE,   FALSE,
7426     EL_WALL_SLIPPERY,                   -1, -1
7427   },
7428   {
7429     Xroundwall_2,                       TRUE,   FALSE,
7430     EL_EMC_WALL_SLIPPERY_2,             -1, -1
7431   },
7432   {
7433     Xroundwall_3,                       TRUE,   FALSE,
7434     EL_EMC_WALL_SLIPPERY_3,             -1, -1
7435   },
7436   {
7437     Xroundwall_4,                       TRUE,   FALSE,
7438     EL_EMC_WALL_SLIPPERY_4,             -1, -1
7439   },
7440
7441   {
7442     Xsteel_1,                           TRUE,   FALSE,
7443     EL_STEELWALL,                       -1, -1
7444   },
7445   {
7446     Xsteel_2,                           TRUE,   FALSE,
7447     EL_EMC_STEELWALL_2,                 -1, -1
7448   },
7449   {
7450     Xsteel_3,                           TRUE,   FALSE,
7451     EL_EMC_STEELWALL_3,                 -1, -1
7452   },
7453   {
7454     Xsteel_4,                           TRUE,   FALSE,
7455     EL_EMC_STEELWALL_4,                 -1, -1
7456   },
7457
7458   {
7459     Xdecor_1,                           TRUE,   FALSE,
7460     EL_EMC_WALL_8,                      -1, -1
7461   },
7462   {
7463     Xdecor_2,                           TRUE,   FALSE,
7464     EL_EMC_WALL_6,                      -1, -1
7465   },
7466   {
7467     Xdecor_3,                           TRUE,   FALSE,
7468     EL_EMC_WALL_4,                      -1, -1
7469   },
7470   {
7471     Xdecor_4,                           TRUE,   FALSE,
7472     EL_EMC_WALL_7,                      -1, -1
7473   },
7474   {
7475     Xdecor_5,                           TRUE,   FALSE,
7476     EL_EMC_WALL_5,                      -1, -1
7477   },
7478   {
7479     Xdecor_6,                           TRUE,   FALSE,
7480     EL_EMC_WALL_9,                      -1, -1
7481   },
7482   {
7483     Xdecor_7,                           TRUE,   FALSE,
7484     EL_EMC_WALL_10,                     -1, -1
7485   },
7486   {
7487     Xdecor_8,                           TRUE,   FALSE,
7488     EL_EMC_WALL_1,                      -1, -1
7489   },
7490   {
7491     Xdecor_9,                           TRUE,   FALSE,
7492     EL_EMC_WALL_2,                      -1, -1
7493   },
7494   {
7495     Xdecor_10,                          TRUE,   FALSE,
7496     EL_EMC_WALL_3,                      -1, -1
7497   },
7498   {
7499     Xdecor_11,                          TRUE,   FALSE,
7500     EL_EMC_WALL_11,                     -1, -1
7501   },
7502   {
7503     Xdecor_12,                          TRUE,   FALSE,
7504     EL_EMC_WALL_12,                     -1, -1
7505   },
7506
7507   {
7508     Xalpha_0,                           TRUE,   FALSE,
7509     EL_CHAR('0'),                       -1, -1
7510   },
7511   {
7512     Xalpha_1,                           TRUE,   FALSE,
7513     EL_CHAR('1'),                       -1, -1
7514   },
7515   {
7516     Xalpha_2,                           TRUE,   FALSE,
7517     EL_CHAR('2'),                       -1, -1
7518   },
7519   {
7520     Xalpha_3,                           TRUE,   FALSE,
7521     EL_CHAR('3'),                       -1, -1
7522   },
7523   {
7524     Xalpha_4,                           TRUE,   FALSE,
7525     EL_CHAR('4'),                       -1, -1
7526   },
7527   {
7528     Xalpha_5,                           TRUE,   FALSE,
7529     EL_CHAR('5'),                       -1, -1
7530   },
7531   {
7532     Xalpha_6,                           TRUE,   FALSE,
7533     EL_CHAR('6'),                       -1, -1
7534   },
7535   {
7536     Xalpha_7,                           TRUE,   FALSE,
7537     EL_CHAR('7'),                       -1, -1
7538   },
7539   {
7540     Xalpha_8,                           TRUE,   FALSE,
7541     EL_CHAR('8'),                       -1, -1
7542   },
7543   {
7544     Xalpha_9,                           TRUE,   FALSE,
7545     EL_CHAR('9'),                       -1, -1
7546   },
7547   {
7548     Xalpha_excla,                       TRUE,   FALSE,
7549     EL_CHAR('!'),                       -1, -1
7550   },
7551   {
7552     Xalpha_apost,                       TRUE,   FALSE,
7553     EL_CHAR('\''),                      -1, -1
7554   },
7555   {
7556     Xalpha_comma,                       TRUE,   FALSE,
7557     EL_CHAR(','),                       -1, -1
7558   },
7559   {
7560     Xalpha_minus,                       TRUE,   FALSE,
7561     EL_CHAR('-'),                       -1, -1
7562   },
7563   {
7564     Xalpha_perio,                       TRUE,   FALSE,
7565     EL_CHAR('.'),                       -1, -1
7566   },
7567   {
7568     Xalpha_colon,                       TRUE,   FALSE,
7569     EL_CHAR(':'),                       -1, -1
7570   },
7571   {
7572     Xalpha_quest,                       TRUE,   FALSE,
7573     EL_CHAR('?'),                       -1, -1
7574   },
7575   {
7576     Xalpha_a,                           TRUE,   FALSE,
7577     EL_CHAR('A'),                       -1, -1
7578   },
7579   {
7580     Xalpha_b,                           TRUE,   FALSE,
7581     EL_CHAR('B'),                       -1, -1
7582   },
7583   {
7584     Xalpha_c,                           TRUE,   FALSE,
7585     EL_CHAR('C'),                       -1, -1
7586   },
7587   {
7588     Xalpha_d,                           TRUE,   FALSE,
7589     EL_CHAR('D'),                       -1, -1
7590   },
7591   {
7592     Xalpha_e,                           TRUE,   FALSE,
7593     EL_CHAR('E'),                       -1, -1
7594   },
7595   {
7596     Xalpha_f,                           TRUE,   FALSE,
7597     EL_CHAR('F'),                       -1, -1
7598   },
7599   {
7600     Xalpha_g,                           TRUE,   FALSE,
7601     EL_CHAR('G'),                       -1, -1
7602   },
7603   {
7604     Xalpha_h,                           TRUE,   FALSE,
7605     EL_CHAR('H'),                       -1, -1
7606   },
7607   {
7608     Xalpha_i,                           TRUE,   FALSE,
7609     EL_CHAR('I'),                       -1, -1
7610   },
7611   {
7612     Xalpha_j,                           TRUE,   FALSE,
7613     EL_CHAR('J'),                       -1, -1
7614   },
7615   {
7616     Xalpha_k,                           TRUE,   FALSE,
7617     EL_CHAR('K'),                       -1, -1
7618   },
7619   {
7620     Xalpha_l,                           TRUE,   FALSE,
7621     EL_CHAR('L'),                       -1, -1
7622   },
7623   {
7624     Xalpha_m,                           TRUE,   FALSE,
7625     EL_CHAR('M'),                       -1, -1
7626   },
7627   {
7628     Xalpha_n,                           TRUE,   FALSE,
7629     EL_CHAR('N'),                       -1, -1
7630   },
7631   {
7632     Xalpha_o,                           TRUE,   FALSE,
7633     EL_CHAR('O'),                       -1, -1
7634   },
7635   {
7636     Xalpha_p,                           TRUE,   FALSE,
7637     EL_CHAR('P'),                       -1, -1
7638   },
7639   {
7640     Xalpha_q,                           TRUE,   FALSE,
7641     EL_CHAR('Q'),                       -1, -1
7642   },
7643   {
7644     Xalpha_r,                           TRUE,   FALSE,
7645     EL_CHAR('R'),                       -1, -1
7646   },
7647   {
7648     Xalpha_s,                           TRUE,   FALSE,
7649     EL_CHAR('S'),                       -1, -1
7650   },
7651   {
7652     Xalpha_t,                           TRUE,   FALSE,
7653     EL_CHAR('T'),                       -1, -1
7654   },
7655   {
7656     Xalpha_u,                           TRUE,   FALSE,
7657     EL_CHAR('U'),                       -1, -1
7658   },
7659   {
7660     Xalpha_v,                           TRUE,   FALSE,
7661     EL_CHAR('V'),                       -1, -1
7662   },
7663   {
7664     Xalpha_w,                           TRUE,   FALSE,
7665     EL_CHAR('W'),                       -1, -1
7666   },
7667   {
7668     Xalpha_x,                           TRUE,   FALSE,
7669     EL_CHAR('X'),                       -1, -1
7670   },
7671   {
7672     Xalpha_y,                           TRUE,   FALSE,
7673     EL_CHAR('Y'),                       -1, -1
7674   },
7675   {
7676     Xalpha_z,                           TRUE,   FALSE,
7677     EL_CHAR('Z'),                       -1, -1
7678   },
7679   {
7680     Xalpha_arrow_e,                     TRUE,   FALSE,
7681     EL_CHAR('>'),                       -1, -1
7682   },
7683   {
7684     Xalpha_arrow_w,                     TRUE,   FALSE,
7685     EL_CHAR('<'),                       -1, -1
7686   },
7687   {
7688     Xalpha_copyr,                       TRUE,   FALSE,
7689     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
7690   },
7691
7692   {
7693     Ykey_1_blank,                       FALSE,  FALSE,
7694     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
7695   },
7696   {
7697     Ykey_2_blank,                       FALSE,  FALSE,
7698     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
7699   },
7700   {
7701     Ykey_3_blank,                       FALSE,  FALSE,
7702     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
7703   },
7704   {
7705     Ykey_4_blank,                       FALSE,  FALSE,
7706     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
7707   },
7708   {
7709     Ykey_5_blank,                       FALSE,  FALSE,
7710     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
7711   },
7712   {
7713     Ykey_6_blank,                       FALSE,  FALSE,
7714     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
7715   },
7716   {
7717     Ykey_7_blank,                       FALSE,  FALSE,
7718     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
7719   },
7720   {
7721     Ykey_8_blank,                       FALSE,  FALSE,
7722     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
7723   },
7724   {
7725     Ylenses_blank,                      FALSE,  FALSE,
7726     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
7727   },
7728   {
7729     Ymagnify_blank,                     FALSE,  FALSE,
7730     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
7731   },
7732   {
7733     Ygrass_blank,                       FALSE,  FALSE,
7734     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
7735   },
7736   {
7737     Ydirt_blank,                        FALSE,  FALSE,
7738     EL_SAND,                            ACTION_SNAPPING, -1
7739   },
7740
7741   {
7742     -1,                                 FALSE,  FALSE,
7743     -1,                                 -1, -1
7744   }
7745 };
7746
7747 static struct Mapping_EM_to_RND_player
7748 {
7749   int action_em;
7750   int player_nr;
7751
7752   int element_rnd;
7753   int action;
7754   int direction;
7755 }
7756 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7757 {
7758   {
7759     PLY_walk_n,                         0,
7760     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
7761   },
7762   {
7763     PLY_walk_e,                         0,
7764     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
7765   },
7766   {
7767     PLY_walk_s,                         0,
7768     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
7769   },
7770   {
7771     PLY_walk_w,                         0,
7772     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
7773   },
7774   {
7775     PLY_push_n,                         0,
7776     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
7777   },
7778   {
7779     PLY_push_e,                         0,
7780     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
7781   },
7782   {
7783     PLY_push_s,                         0,
7784     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
7785   },
7786   {
7787     PLY_push_w,                         0,
7788     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
7789   },
7790   {
7791     PLY_shoot_n,                        0,
7792     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
7793   },
7794   {
7795     PLY_shoot_e,                        0,
7796     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7797   },
7798   {
7799     PLY_shoot_s,                        0,
7800     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
7801   },
7802   {
7803     PLY_shoot_w,                        0,
7804     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
7805   },
7806   {
7807     PLY_walk_n,                         1,
7808     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
7809   },
7810   {
7811     PLY_walk_e,                         1,
7812     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
7813   },
7814   {
7815     PLY_walk_s,                         1,
7816     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
7817   },
7818   {
7819     PLY_walk_w,                         1,
7820     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
7821   },
7822   {
7823     PLY_push_n,                         1,
7824     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
7825   },
7826   {
7827     PLY_push_e,                         1,
7828     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
7829   },
7830   {
7831     PLY_push_s,                         1,
7832     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
7833   },
7834   {
7835     PLY_push_w,                         1,
7836     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
7837   },
7838   {
7839     PLY_shoot_n,                        1,
7840     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
7841   },
7842   {
7843     PLY_shoot_e,                        1,
7844     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7845   },
7846   {
7847     PLY_shoot_s,                        1,
7848     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
7849   },
7850   {
7851     PLY_shoot_w,                        1,
7852     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
7853   },
7854   {
7855     PLY_still,                          0,
7856     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
7857   },
7858   {
7859     PLY_still,                          1,
7860     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
7861   },
7862   {
7863     PLY_walk_n,                         2,
7864     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
7865   },
7866   {
7867     PLY_walk_e,                         2,
7868     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
7869   },
7870   {
7871     PLY_walk_s,                         2,
7872     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
7873   },
7874   {
7875     PLY_walk_w,                         2,
7876     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
7877   },
7878   {
7879     PLY_push_n,                         2,
7880     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
7881   },
7882   {
7883     PLY_push_e,                         2,
7884     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
7885   },
7886   {
7887     PLY_push_s,                         2,
7888     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
7889   },
7890   {
7891     PLY_push_w,                         2,
7892     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
7893   },
7894   {
7895     PLY_shoot_n,                        2,
7896     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
7897   },
7898   {
7899     PLY_shoot_e,                        2,
7900     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7901   },
7902   {
7903     PLY_shoot_s,                        2,
7904     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
7905   },
7906   {
7907     PLY_shoot_w,                        2,
7908     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
7909   },
7910   {
7911     PLY_walk_n,                         3,
7912     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
7913   },
7914   {
7915     PLY_walk_e,                         3,
7916     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
7917   },
7918   {
7919     PLY_walk_s,                         3,
7920     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
7921   },
7922   {
7923     PLY_walk_w,                         3,
7924     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
7925   },
7926   {
7927     PLY_push_n,                         3,
7928     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
7929   },
7930   {
7931     PLY_push_e,                         3,
7932     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
7933   },
7934   {
7935     PLY_push_s,                         3,
7936     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
7937   },
7938   {
7939     PLY_push_w,                         3,
7940     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
7941   },
7942   {
7943     PLY_shoot_n,                        3,
7944     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
7945   },
7946   {
7947     PLY_shoot_e,                        3,
7948     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7949   },
7950   {
7951     PLY_shoot_s,                        3,
7952     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
7953   },
7954   {
7955     PLY_shoot_w,                        3,
7956     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
7957   },
7958   {
7959     PLY_still,                          2,
7960     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
7961   },
7962   {
7963     PLY_still,                          3,
7964     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7965   },
7966
7967   {
7968     -1,                                 -1,
7969     -1,                                 -1, -1
7970   }
7971 };
7972
7973 int map_element_RND_to_EM_cave(int element_rnd)
7974 {
7975   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7976   static boolean mapping_initialized = FALSE;
7977
7978   if (!mapping_initialized)
7979   {
7980     int i;
7981
7982     // return "Xalpha_quest" for all undefined elements in mapping array
7983     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7984       mapping_RND_to_EM[i] = Xalpha_quest;
7985
7986     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7987       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7988         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7989           em_object_mapping_list[i].element_em;
7990
7991     mapping_initialized = TRUE;
7992   }
7993
7994   if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7995   {
7996     Warn("invalid RND level element %d", element_rnd);
7997
7998     return EL_UNKNOWN;
7999   }
8000
8001   return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8002 }
8003
8004 int map_element_EM_to_RND_cave(int element_em_cave)
8005 {
8006   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8007   static boolean mapping_initialized = FALSE;
8008
8009   if (!mapping_initialized)
8010   {
8011     int i;
8012
8013     // return "EL_UNKNOWN" for all undefined elements in mapping array
8014     for (i = 0; i < GAME_TILE_MAX; i++)
8015       mapping_EM_to_RND[i] = EL_UNKNOWN;
8016
8017     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8018       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8019         em_object_mapping_list[i].element_rnd;
8020
8021     mapping_initialized = TRUE;
8022   }
8023
8024   if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8025   {
8026     Warn("invalid EM cave element %d", element_em_cave);
8027
8028     return EL_UNKNOWN;
8029   }
8030
8031   return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8032 }
8033
8034 int map_element_EM_to_RND_game(int element_em_game)
8035 {
8036   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8037   static boolean mapping_initialized = FALSE;
8038
8039   if (!mapping_initialized)
8040   {
8041     int i;
8042
8043     // return "EL_UNKNOWN" for all undefined elements in mapping array
8044     for (i = 0; i < GAME_TILE_MAX; i++)
8045       mapping_EM_to_RND[i] = EL_UNKNOWN;
8046
8047     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8048       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8049         em_object_mapping_list[i].element_rnd;
8050
8051     mapping_initialized = TRUE;
8052   }
8053
8054   if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8055   {
8056     Warn("invalid EM game element %d", element_em_game);
8057
8058     return EL_UNKNOWN;
8059   }
8060
8061   return mapping_EM_to_RND[element_em_game];
8062 }
8063
8064 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8065 {
8066   struct LevelInfo_EM *level_em = level->native_em_level;
8067   struct CAVE *cav = level_em->cav;
8068   int i, j;
8069
8070   for (i = 0; i < GAME_TILE_MAX; i++)
8071     cav->android_array[i] = Cblank;
8072
8073   for (i = 0; i < level->num_android_clone_elements; i++)
8074   {
8075     int element_rnd = level->android_clone_element[i];
8076     int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8077
8078     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8079       if (em_object_mapping_list[j].element_rnd == element_rnd)
8080         cav->android_array[em_object_mapping_list[j].element_em] =
8081           element_em_cave;
8082   }
8083 }
8084
8085 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8086 {
8087   struct LevelInfo_EM *level_em = level->native_em_level;
8088   struct CAVE *cav = level_em->cav;
8089   int i, j;
8090
8091   level->num_android_clone_elements = 0;
8092
8093   for (i = 0; i < GAME_TILE_MAX; i++)
8094   {
8095     int element_em_cave = cav->android_array[i];
8096     int element_rnd;
8097     boolean element_found = FALSE;
8098
8099     if (element_em_cave == Cblank)
8100       continue;
8101
8102     element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8103
8104     for (j = 0; j < level->num_android_clone_elements; j++)
8105       if (level->android_clone_element[j] == element_rnd)
8106         element_found = TRUE;
8107
8108     if (!element_found)
8109     {
8110       level->android_clone_element[level->num_android_clone_elements++] =
8111         element_rnd;
8112
8113       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8114         break;
8115     }
8116   }
8117
8118   if (level->num_android_clone_elements == 0)
8119   {
8120     level->num_android_clone_elements = 1;
8121     level->android_clone_element[0] = EL_EMPTY;
8122   }
8123 }
8124
8125 int map_direction_RND_to_EM(int direction)
8126 {
8127   return (direction == MV_UP    ? 0 :
8128           direction == MV_RIGHT ? 1 :
8129           direction == MV_DOWN  ? 2 :
8130           direction == MV_LEFT  ? 3 :
8131           -1);
8132 }
8133
8134 int map_direction_EM_to_RND(int direction)
8135 {
8136   return (direction == 0 ? MV_UP    :
8137           direction == 1 ? MV_RIGHT :
8138           direction == 2 ? MV_DOWN  :
8139           direction == 3 ? MV_LEFT  :
8140           MV_NONE);
8141 }
8142
8143 int map_element_RND_to_SP(int element_rnd)
8144 {
8145   int element_sp = 0x20;        // map unknown elements to yellow "hardware"
8146
8147   if (element_rnd >= EL_SP_START &&
8148       element_rnd <= EL_SP_END)
8149     element_sp = element_rnd - EL_SP_START;
8150   else if (element_rnd == EL_EMPTY_SPACE)
8151     element_sp = 0x00;
8152   else if (element_rnd == EL_INVISIBLE_WALL)
8153     element_sp = 0x28;
8154
8155   return element_sp;
8156 }
8157
8158 int map_element_SP_to_RND(int element_sp)
8159 {
8160   int element_rnd = EL_UNKNOWN;
8161
8162   if (element_sp >= 0x00 &&
8163       element_sp <= 0x27)
8164     element_rnd = EL_SP_START + element_sp;
8165   else if (element_sp == 0x28)
8166     element_rnd = EL_INVISIBLE_WALL;
8167
8168   return element_rnd;
8169 }
8170
8171 int map_action_SP_to_RND(int action_sp)
8172 {
8173   switch (action_sp)
8174   {
8175     case actActive:             return ACTION_ACTIVE;
8176     case actImpact:             return ACTION_IMPACT;
8177     case actExploding:          return ACTION_EXPLODING;
8178     case actDigging:            return ACTION_DIGGING;
8179     case actSnapping:           return ACTION_SNAPPING;
8180     case actCollecting:         return ACTION_COLLECTING;
8181     case actPassing:            return ACTION_PASSING;
8182     case actPushing:            return ACTION_PUSHING;
8183     case actDropping:           return ACTION_DROPPING;
8184
8185     default:                    return ACTION_DEFAULT;
8186   }
8187 }
8188
8189 int map_element_RND_to_MM(int element_rnd)
8190 {
8191   return (element_rnd >= EL_MM_START_1 &&
8192           element_rnd <= EL_MM_END_1 ?
8193           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8194
8195           element_rnd >= EL_MM_START_2 &&
8196           element_rnd <= EL_MM_END_2 ?
8197           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8198
8199           element_rnd >= EL_CHAR_START &&
8200           element_rnd <= EL_CHAR_END ?
8201           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8202
8203           element_rnd >= EL_MM_RUNTIME_START &&
8204           element_rnd <= EL_MM_RUNTIME_END ?
8205           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8206
8207           EL_MM_EMPTY_NATIVE);
8208 }
8209
8210 int map_element_MM_to_RND(int element_mm)
8211 {
8212   return (element_mm == EL_MM_EMPTY_NATIVE ||
8213           element_mm == EL_DF_EMPTY_NATIVE ?
8214           EL_EMPTY :
8215
8216           element_mm >= EL_MM_START_1_NATIVE &&
8217           element_mm <= EL_MM_END_1_NATIVE ?
8218           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8219
8220           element_mm >= EL_MM_START_2_NATIVE &&
8221           element_mm <= EL_MM_END_2_NATIVE ?
8222           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8223
8224           element_mm >= EL_MM_CHAR_START_NATIVE &&
8225           element_mm <= EL_MM_CHAR_END_NATIVE ?
8226           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8227
8228           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8229           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8230           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8231
8232           EL_EMPTY);
8233 }
8234
8235 int map_action_MM_to_RND(int action_mm)
8236 {
8237   // all MM actions are defined to exactly match their RND counterparts
8238   return action_mm;
8239 }
8240
8241 int map_sound_MM_to_RND(int sound_mm)
8242 {
8243   switch (sound_mm)
8244   {
8245     case SND_MM_GAME_LEVELTIME_CHARGING:
8246       return SND_GAME_LEVELTIME_CHARGING;
8247
8248     case SND_MM_GAME_HEALTH_CHARGING:
8249       return SND_GAME_HEALTH_CHARGING;
8250
8251     default:
8252       return SND_UNDEFINED;
8253   }
8254 }
8255
8256 int map_mm_wall_element(int element)
8257 {
8258   return (element >= EL_MM_STEEL_WALL_START &&
8259           element <= EL_MM_STEEL_WALL_END ?
8260           EL_MM_STEEL_WALL :
8261
8262           element >= EL_MM_WOODEN_WALL_START &&
8263           element <= EL_MM_WOODEN_WALL_END ?
8264           EL_MM_WOODEN_WALL :
8265
8266           element >= EL_MM_ICE_WALL_START &&
8267           element <= EL_MM_ICE_WALL_END ?
8268           EL_MM_ICE_WALL :
8269
8270           element >= EL_MM_AMOEBA_WALL_START &&
8271           element <= EL_MM_AMOEBA_WALL_END ?
8272           EL_MM_AMOEBA_WALL :
8273
8274           element >= EL_DF_STEEL_WALL_START &&
8275           element <= EL_DF_STEEL_WALL_END ?
8276           EL_DF_STEEL_WALL :
8277
8278           element >= EL_DF_WOODEN_WALL_START &&
8279           element <= EL_DF_WOODEN_WALL_END ?
8280           EL_DF_WOODEN_WALL :
8281
8282           element);
8283 }
8284
8285 int map_mm_wall_element_editor(int element)
8286 {
8287   switch (element)
8288   {
8289     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
8290     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
8291     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
8292     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
8293     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
8294     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
8295
8296     default:                    return element;
8297   }
8298 }
8299
8300 int get_next_element(int element)
8301 {
8302   switch (element)
8303   {
8304     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
8305     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
8306     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
8307     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
8308     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
8309     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
8310     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
8311     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
8312     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
8313     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
8314     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
8315
8316     default:                            return element;
8317   }
8318 }
8319
8320 int el2img_mm(int element_mm)
8321 {
8322   return el2img(map_element_MM_to_RND(element_mm));
8323 }
8324
8325 int el_act2img_mm(int element_mm, int action)
8326 {
8327   return el_act2img(map_element_MM_to_RND(element_mm), action);
8328 }
8329
8330 int el_act_dir2img(int element, int action, int direction)
8331 {
8332   element = GFX_ELEMENT(element);
8333   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8334
8335   // direction_graphic[][] == graphic[] for undefined direction graphics
8336   return element_info[element].direction_graphic[action][direction];
8337 }
8338
8339 static int el_act_dir2crm(int element, int action, int direction)
8340 {
8341   element = GFX_ELEMENT(element);
8342   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8343
8344   // direction_graphic[][] == graphic[] for undefined direction graphics
8345   return element_info[element].direction_crumbled[action][direction];
8346 }
8347
8348 int el_act2img(int element, int action)
8349 {
8350   element = GFX_ELEMENT(element);
8351
8352   return element_info[element].graphic[action];
8353 }
8354
8355 int el_act2crm(int element, int action)
8356 {
8357   element = GFX_ELEMENT(element);
8358
8359   return element_info[element].crumbled[action];
8360 }
8361
8362 int el_dir2img(int element, int direction)
8363 {
8364   element = GFX_ELEMENT(element);
8365
8366   return el_act_dir2img(element, ACTION_DEFAULT, direction);
8367 }
8368
8369 int el2baseimg(int element)
8370 {
8371   return element_info[element].graphic[ACTION_DEFAULT];
8372 }
8373
8374 int el2img(int element)
8375 {
8376   element = GFX_ELEMENT(element);
8377
8378   return element_info[element].graphic[ACTION_DEFAULT];
8379 }
8380
8381 int el2edimg(int element)
8382 {
8383   element = GFX_ELEMENT(element);
8384
8385   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8386 }
8387
8388 int el2preimg(int element)
8389 {
8390   element = GFX_ELEMENT(element);
8391
8392   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8393 }
8394
8395 int el2panelimg(int element)
8396 {
8397   element = GFX_ELEMENT(element);
8398
8399   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8400 }
8401
8402 int font2baseimg(int font_nr)
8403 {
8404   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8405 }
8406
8407 int getBeltNrFromBeltElement(int element)
8408 {
8409   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8410           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8411           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8412 }
8413
8414 int getBeltNrFromBeltActiveElement(int element)
8415 {
8416   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8417           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8418           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8419 }
8420
8421 int getBeltNrFromBeltSwitchElement(int element)
8422 {
8423   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8424           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8425           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8426 }
8427
8428 int getBeltDirNrFromBeltElement(int element)
8429 {
8430   static int belt_base_element[4] =
8431   {
8432     EL_CONVEYOR_BELT_1_LEFT,
8433     EL_CONVEYOR_BELT_2_LEFT,
8434     EL_CONVEYOR_BELT_3_LEFT,
8435     EL_CONVEYOR_BELT_4_LEFT
8436   };
8437
8438   int belt_nr = getBeltNrFromBeltElement(element);
8439   int belt_dir_nr = element - belt_base_element[belt_nr];
8440
8441   return (belt_dir_nr % 3);
8442 }
8443
8444 int getBeltDirNrFromBeltSwitchElement(int element)
8445 {
8446   static int belt_base_element[4] =
8447   {
8448     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8449     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8450     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8451     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8452   };
8453
8454   int belt_nr = getBeltNrFromBeltSwitchElement(element);
8455   int belt_dir_nr = element - belt_base_element[belt_nr];
8456
8457   return (belt_dir_nr % 3);
8458 }
8459
8460 int getBeltDirFromBeltElement(int element)
8461 {
8462   static int belt_move_dir[3] =
8463   {
8464     MV_LEFT,
8465     MV_NONE,
8466     MV_RIGHT
8467   };
8468
8469   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8470
8471   return belt_move_dir[belt_dir_nr];
8472 }
8473
8474 int getBeltDirFromBeltSwitchElement(int element)
8475 {
8476   static int belt_move_dir[3] =
8477   {
8478     MV_LEFT,
8479     MV_NONE,
8480     MV_RIGHT
8481   };
8482
8483   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8484
8485   return belt_move_dir[belt_dir_nr];
8486 }
8487
8488 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8489 {
8490   static int belt_base_element[4] =
8491   {
8492     EL_CONVEYOR_BELT_1_LEFT,
8493     EL_CONVEYOR_BELT_2_LEFT,
8494     EL_CONVEYOR_BELT_3_LEFT,
8495     EL_CONVEYOR_BELT_4_LEFT
8496   };
8497
8498   return belt_base_element[belt_nr] + belt_dir_nr;
8499 }
8500
8501 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8502 {
8503   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8504
8505   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8506 }
8507
8508 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8509 {
8510   static int belt_base_element[4] =
8511   {
8512     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8513     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8514     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8515     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8516   };
8517
8518   return belt_base_element[belt_nr] + belt_dir_nr;
8519 }
8520
8521 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8522 {
8523   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8524
8525   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8526 }
8527
8528 boolean swapTiles_EM(boolean is_pre_emc_cave)
8529 {
8530   return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8531 }
8532
8533 boolean getTeamMode_EM(void)
8534 {
8535   return game.team_mode || network_playing;
8536 }
8537
8538 boolean isActivePlayer_EM(int player_nr)
8539 {
8540   return stored_player[player_nr].active;
8541 }
8542
8543 unsigned int InitRND(int seed)
8544 {
8545   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8546     return InitEngineRandom_EM(seed);
8547   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8548     return InitEngineRandom_SP(seed);
8549   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8550     return InitEngineRandom_MM(seed);
8551   else
8552     return InitEngineRandom_RND(seed);
8553 }
8554
8555 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8556 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8557
8558 static int get_effective_element_EM(int tile, int frame_em)
8559 {
8560   int element             = object_mapping[tile].element_rnd;
8561   int action              = object_mapping[tile].action;
8562   boolean is_backside     = object_mapping[tile].is_backside;
8563   boolean action_removing = (action == ACTION_DIGGING ||
8564                              action == ACTION_SNAPPING ||
8565                              action == ACTION_COLLECTING);
8566
8567   if (frame_em < 7)
8568   {
8569     switch (tile)
8570     {
8571       case Xsplash_e:
8572       case Xsplash_w:
8573         return (frame_em > 5 ? EL_EMPTY : element);
8574
8575       default:
8576         return element;
8577     }
8578   }
8579   else  // frame_em == 7
8580   {
8581     switch (tile)
8582     {
8583       case Xsplash_e:
8584       case Xsplash_w:
8585         return EL_EMPTY;
8586
8587       case Ynut_stone:
8588         return EL_EMERALD;
8589
8590       case Ydiamond_stone:
8591         return EL_ROCK;
8592
8593       case Xdrip_stretch:
8594       case Xdrip_stretchB:
8595       case Ydrip_1_s:
8596       case Ydrip_1_sB:
8597       case Yball_1:
8598       case Xball_2:
8599       case Yball_2:
8600       case Yball_blank:
8601       case Ykey_1_blank:
8602       case Ykey_2_blank:
8603       case Ykey_3_blank:
8604       case Ykey_4_blank:
8605       case Ykey_5_blank:
8606       case Ykey_6_blank:
8607       case Ykey_7_blank:
8608       case Ykey_8_blank:
8609       case Ylenses_blank:
8610       case Ymagnify_blank:
8611       case Ygrass_blank:
8612       case Ydirt_blank:
8613       case Xsand_stonein_1:
8614       case Xsand_stonein_2:
8615       case Xsand_stonein_3:
8616       case Xsand_stonein_4:
8617         return element;
8618
8619       default:
8620         return (is_backside || action_removing ? EL_EMPTY : element);
8621     }
8622   }
8623 }
8624
8625 static boolean check_linear_animation_EM(int tile)
8626 {
8627   switch (tile)
8628   {
8629     case Xsand_stonesand_1:
8630     case Xsand_stonesand_quickout_1:
8631     case Xsand_sandstone_1:
8632     case Xsand_stonein_1:
8633     case Xsand_stoneout_1:
8634     case Xboom_1:
8635     case Xdynamite_1:
8636     case Ybug_w_n:
8637     case Ybug_n_e:
8638     case Ybug_e_s:
8639     case Ybug_s_w:
8640     case Ybug_e_n:
8641     case Ybug_s_e:
8642     case Ybug_w_s:
8643     case Ybug_n_w:
8644     case Ytank_w_n:
8645     case Ytank_n_e:
8646     case Ytank_e_s:
8647     case Ytank_s_w:
8648     case Ytank_e_n:
8649     case Ytank_s_e:
8650     case Ytank_w_s:
8651     case Ytank_n_w:
8652     case Xsplash_e:
8653     case Xsplash_w:
8654     case Ynut_stone:
8655       return TRUE;
8656   }
8657
8658   return FALSE;
8659 }
8660
8661 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8662                                      boolean has_crumbled_graphics,
8663                                      int crumbled, int sync_frame)
8664 {
8665   // if element can be crumbled, but certain action graphics are just empty
8666   // space (like instantly snapping sand to empty space in 1 frame), do not
8667   // treat these empty space graphics as crumbled graphics in EMC engine
8668   if (crumbled == IMG_EMPTY_SPACE)
8669     has_crumbled_graphics = FALSE;
8670
8671   if (has_crumbled_graphics)
8672   {
8673     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8674     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8675                                            g_crumbled->anim_delay,
8676                                            g_crumbled->anim_mode,
8677                                            g_crumbled->anim_start_frame,
8678                                            sync_frame);
8679
8680     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8681                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8682
8683     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8684     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8685
8686     g_em->has_crumbled_graphics = TRUE;
8687   }
8688   else
8689   {
8690     g_em->crumbled_bitmap = NULL;
8691     g_em->crumbled_src_x = 0;
8692     g_em->crumbled_src_y = 0;
8693     g_em->crumbled_border_size = 0;
8694     g_em->crumbled_tile_size = 0;
8695
8696     g_em->has_crumbled_graphics = FALSE;
8697   }
8698 }
8699
8700 #if 0
8701 void ResetGfxAnimation_EM(int x, int y, int tile)
8702 {
8703   GfxFrame[x][y] = 0;
8704 }
8705 #endif
8706
8707 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8708                         int tile, int frame_em, int x, int y)
8709 {
8710   int action = object_mapping[tile].action;
8711   int direction = object_mapping[tile].direction;
8712   int effective_element = get_effective_element_EM(tile, frame_em);
8713   int graphic = (direction == MV_NONE ?
8714                  el_act2img(effective_element, action) :
8715                  el_act_dir2img(effective_element, action, direction));
8716   struct GraphicInfo *g = &graphic_info[graphic];
8717   int sync_frame;
8718   boolean action_removing = (action == ACTION_DIGGING ||
8719                              action == ACTION_SNAPPING ||
8720                              action == ACTION_COLLECTING);
8721   boolean action_moving   = (action == ACTION_FALLING ||
8722                              action == ACTION_MOVING ||
8723                              action == ACTION_PUSHING ||
8724                              action == ACTION_EATING ||
8725                              action == ACTION_FILLING ||
8726                              action == ACTION_EMPTYING);
8727   boolean action_falling  = (action == ACTION_FALLING ||
8728                              action == ACTION_FILLING ||
8729                              action == ACTION_EMPTYING);
8730
8731   // special case: graphic uses "2nd movement tile" and has defined
8732   // 7 frames for movement animation (or less) => use default graphic
8733   // for last (8th) frame which ends the movement animation
8734   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8735   {
8736     action = ACTION_DEFAULT;    // (keep action_* unchanged for now)
8737     graphic = (direction == MV_NONE ?
8738                el_act2img(effective_element, action) :
8739                el_act_dir2img(effective_element, action, direction));
8740
8741     g = &graphic_info[graphic];
8742   }
8743
8744   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8745   {
8746     GfxFrame[x][y] = 0;
8747   }
8748   else if (action_moving)
8749   {
8750     boolean is_backside = object_mapping[tile].is_backside;
8751
8752     if (is_backside)
8753     {
8754       int direction = object_mapping[tile].direction;
8755       int move_dir = (action_falling ? MV_DOWN : direction);
8756
8757       GfxFrame[x][y]++;
8758
8759 #if 1
8760       // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8761       if (g->double_movement && frame_em == 0)
8762         GfxFrame[x][y] = 0;
8763 #endif
8764
8765       if (move_dir == MV_LEFT)
8766         GfxFrame[x - 1][y] = GfxFrame[x][y];
8767       else if (move_dir == MV_RIGHT)
8768         GfxFrame[x + 1][y] = GfxFrame[x][y];
8769       else if (move_dir == MV_UP)
8770         GfxFrame[x][y - 1] = GfxFrame[x][y];
8771       else if (move_dir == MV_DOWN)
8772         GfxFrame[x][y + 1] = GfxFrame[x][y];
8773     }
8774   }
8775   else
8776   {
8777     GfxFrame[x][y]++;
8778
8779     // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8780     if (tile == Xsand_stonesand_quickout_1 ||
8781         tile == Xsand_stonesand_quickout_2)
8782       GfxFrame[x][y]++;
8783   }
8784
8785   if (graphic_info[graphic].anim_global_sync)
8786     sync_frame = FrameCounter;
8787   else if (graphic_info[graphic].anim_global_anim_sync)
8788     sync_frame = getGlobalAnimSyncFrame();
8789   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8790     sync_frame = GfxFrame[x][y];
8791   else
8792     sync_frame = 0;     // playfield border (pseudo steel)
8793
8794   SetRandomAnimationValue(x, y);
8795
8796   int frame = getAnimationFrame(g->anim_frames,
8797                                 g->anim_delay,
8798                                 g->anim_mode,
8799                                 g->anim_start_frame,
8800                                 sync_frame);
8801
8802   g_em->unique_identifier =
8803     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8804 }
8805
8806 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8807                                   int tile, int frame_em, int x, int y)
8808 {
8809   int action = object_mapping[tile].action;
8810   int direction = object_mapping[tile].direction;
8811   boolean is_backside = object_mapping[tile].is_backside;
8812   int effective_element = get_effective_element_EM(tile, frame_em);
8813   int effective_action = action;
8814   int graphic = (direction == MV_NONE ?
8815                  el_act2img(effective_element, effective_action) :
8816                  el_act_dir2img(effective_element, effective_action,
8817                                 direction));
8818   int crumbled = (direction == MV_NONE ?
8819                   el_act2crm(effective_element, effective_action) :
8820                   el_act_dir2crm(effective_element, effective_action,
8821                                  direction));
8822   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8823   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8824   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8825   struct GraphicInfo *g = &graphic_info[graphic];
8826   int sync_frame;
8827
8828   // special case: graphic uses "2nd movement tile" and has defined
8829   // 7 frames for movement animation (or less) => use default graphic
8830   // for last (8th) frame which ends the movement animation
8831   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8832   {
8833     effective_action = ACTION_DEFAULT;
8834     graphic = (direction == MV_NONE ?
8835                el_act2img(effective_element, effective_action) :
8836                el_act_dir2img(effective_element, effective_action,
8837                               direction));
8838     crumbled = (direction == MV_NONE ?
8839                 el_act2crm(effective_element, effective_action) :
8840                 el_act_dir2crm(effective_element, effective_action,
8841                                direction));
8842
8843     g = &graphic_info[graphic];
8844   }
8845
8846   if (graphic_info[graphic].anim_global_sync)
8847     sync_frame = FrameCounter;
8848   else if (graphic_info[graphic].anim_global_anim_sync)
8849     sync_frame = getGlobalAnimSyncFrame();
8850   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8851     sync_frame = GfxFrame[x][y];
8852   else
8853     sync_frame = 0;     // playfield border (pseudo steel)
8854
8855   SetRandomAnimationValue(x, y);
8856
8857   int frame = getAnimationFrame(g->anim_frames,
8858                                 g->anim_delay,
8859                                 g->anim_mode,
8860                                 g->anim_start_frame,
8861                                 sync_frame);
8862
8863   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8864                       g->double_movement && is_backside);
8865
8866   // (updating the "crumbled" graphic definitions is probably not really needed,
8867   // as animations for crumbled graphics can't be longer than one EMC cycle)
8868   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8869                            sync_frame);
8870 }
8871
8872 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8873                                   int player_nr, int anim, int frame_em)
8874 {
8875   int element   = player_mapping[player_nr][anim].element_rnd;
8876   int action    = player_mapping[player_nr][anim].action;
8877   int direction = player_mapping[player_nr][anim].direction;
8878   int graphic = (direction == MV_NONE ?
8879                  el_act2img(element, action) :
8880                  el_act_dir2img(element, action, direction));
8881   struct GraphicInfo *g = &graphic_info[graphic];
8882   int sync_frame;
8883
8884   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8885
8886   stored_player[player_nr].StepFrame = frame_em;
8887
8888   sync_frame = stored_player[player_nr].Frame;
8889
8890   int frame = getAnimationFrame(g->anim_frames,
8891                                 g->anim_delay,
8892                                 g->anim_mode,
8893                                 g->anim_start_frame,
8894                                 sync_frame);
8895
8896   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8897                       &g_em->src_x, &g_em->src_y, FALSE);
8898 }
8899
8900 void InitGraphicInfo_EM(void)
8901 {
8902   int i, j, p;
8903
8904   // always start with reliable default values
8905   for (i = 0; i < GAME_TILE_MAX; i++)
8906   {
8907     object_mapping[i].element_rnd = EL_UNKNOWN;
8908     object_mapping[i].is_backside = FALSE;
8909     object_mapping[i].action = ACTION_DEFAULT;
8910     object_mapping[i].direction = MV_NONE;
8911   }
8912
8913   // always start with reliable default values
8914   for (p = 0; p < MAX_PLAYERS; p++)
8915   {
8916     for (i = 0; i < PLY_MAX; i++)
8917     {
8918       player_mapping[p][i].element_rnd = EL_UNKNOWN;
8919       player_mapping[p][i].action = ACTION_DEFAULT;
8920       player_mapping[p][i].direction = MV_NONE;
8921     }
8922   }
8923
8924   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8925   {
8926     int e = em_object_mapping_list[i].element_em;
8927
8928     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8929     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8930
8931     if (em_object_mapping_list[i].action != -1)
8932       object_mapping[e].action = em_object_mapping_list[i].action;
8933
8934     if (em_object_mapping_list[i].direction != -1)
8935       object_mapping[e].direction =
8936         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8937   }
8938
8939   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8940   {
8941     int a = em_player_mapping_list[i].action_em;
8942     int p = em_player_mapping_list[i].player_nr;
8943
8944     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8945
8946     if (em_player_mapping_list[i].action != -1)
8947       player_mapping[p][a].action = em_player_mapping_list[i].action;
8948
8949     if (em_player_mapping_list[i].direction != -1)
8950       player_mapping[p][a].direction =
8951         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8952   }
8953
8954   for (i = 0; i < GAME_TILE_MAX; i++)
8955   {
8956     int element = object_mapping[i].element_rnd;
8957     int action = object_mapping[i].action;
8958     int direction = object_mapping[i].direction;
8959     boolean is_backside = object_mapping[i].is_backside;
8960     boolean action_exploding = ((action == ACTION_EXPLODING ||
8961                                  action == ACTION_SMASHED_BY_ROCK ||
8962                                  action == ACTION_SMASHED_BY_SPRING) &&
8963                                 element != EL_DIAMOND);
8964     boolean action_active = (action == ACTION_ACTIVE);
8965     boolean action_other = (action == ACTION_OTHER);
8966
8967     for (j = 0; j < 8; j++)
8968     {
8969       int effective_element = get_effective_element_EM(i, j);
8970       int effective_action = (j < 7 ? action :
8971                               i == Xdrip_stretch ? action :
8972                               i == Xdrip_stretchB ? action :
8973                               i == Ydrip_1_s ? action :
8974                               i == Ydrip_1_sB ? action :
8975                               i == Yball_1 ? action :
8976                               i == Xball_2 ? action :
8977                               i == Yball_2 ? action :
8978                               i == Yball_blank ? action :
8979                               i == Ykey_1_blank ? action :
8980                               i == Ykey_2_blank ? action :
8981                               i == Ykey_3_blank ? action :
8982                               i == Ykey_4_blank ? action :
8983                               i == Ykey_5_blank ? action :
8984                               i == Ykey_6_blank ? action :
8985                               i == Ykey_7_blank ? action :
8986                               i == Ykey_8_blank ? action :
8987                               i == Ylenses_blank ? action :
8988                               i == Ymagnify_blank ? action :
8989                               i == Ygrass_blank ? action :
8990                               i == Ydirt_blank ? action :
8991                               i == Xsand_stonein_1 ? action :
8992                               i == Xsand_stonein_2 ? action :
8993                               i == Xsand_stonein_3 ? action :
8994                               i == Xsand_stonein_4 ? action :
8995                               i == Xsand_stoneout_1 ? action :
8996                               i == Xsand_stoneout_2 ? action :
8997                               i == Xboom_android ? ACTION_EXPLODING :
8998                               action_exploding ? ACTION_EXPLODING :
8999                               action_active ? action :
9000                               action_other ? action :
9001                               ACTION_DEFAULT);
9002       int graphic = (el_act_dir2img(effective_element, effective_action,
9003                                     direction));
9004       int crumbled = (el_act_dir2crm(effective_element, effective_action,
9005                                      direction));
9006       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9007       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9008       boolean has_action_graphics = (graphic != base_graphic);
9009       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9010       struct GraphicInfo *g = &graphic_info[graphic];
9011       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9012       Bitmap *src_bitmap;
9013       int src_x, src_y;
9014       // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9015       boolean special_animation = (action != ACTION_DEFAULT &&
9016                                    g->anim_frames == 3 &&
9017                                    g->anim_delay == 2 &&
9018                                    g->anim_mode & ANIM_LINEAR);
9019       int sync_frame = (i == Xdrip_stretch ? 7 :
9020                         i == Xdrip_stretchB ? 7 :
9021                         i == Ydrip_2_s ? j + 8 :
9022                         i == Ydrip_2_sB ? j + 8 :
9023                         i == Xacid_1 ? 0 :
9024                         i == Xacid_2 ? 10 :
9025                         i == Xacid_3 ? 20 :
9026                         i == Xacid_4 ? 30 :
9027                         i == Xacid_5 ? 40 :
9028                         i == Xacid_6 ? 50 :
9029                         i == Xacid_7 ? 60 :
9030                         i == Xacid_8 ? 70 :
9031                         i == Xfake_acid_1 ? 0 :
9032                         i == Xfake_acid_2 ? 10 :
9033                         i == Xfake_acid_3 ? 20 :
9034                         i == Xfake_acid_4 ? 30 :
9035                         i == Xfake_acid_5 ? 40 :
9036                         i == Xfake_acid_6 ? 50 :
9037                         i == Xfake_acid_7 ? 60 :
9038                         i == Xfake_acid_8 ? 70 :
9039                         i == Xfake_acid_1_player ? 0 :
9040                         i == Xfake_acid_2_player ? 10 :
9041                         i == Xfake_acid_3_player ? 20 :
9042                         i == Xfake_acid_4_player ? 30 :
9043                         i == Xfake_acid_5_player ? 40 :
9044                         i == Xfake_acid_6_player ? 50 :
9045                         i == Xfake_acid_7_player ? 60 :
9046                         i == Xfake_acid_8_player ? 70 :
9047                         i == Xball_2 ? 7 :
9048                         i == Yball_2 ? j + 8 :
9049                         i == Yball_blank ? j + 1 :
9050                         i == Ykey_1_blank ? j + 1 :
9051                         i == Ykey_2_blank ? j + 1 :
9052                         i == Ykey_3_blank ? j + 1 :
9053                         i == Ykey_4_blank ? j + 1 :
9054                         i == Ykey_5_blank ? j + 1 :
9055                         i == Ykey_6_blank ? j + 1 :
9056                         i == Ykey_7_blank ? j + 1 :
9057                         i == Ykey_8_blank ? j + 1 :
9058                         i == Ylenses_blank ? j + 1 :
9059                         i == Ymagnify_blank ? j + 1 :
9060                         i == Ygrass_blank ? j + 1 :
9061                         i == Ydirt_blank ? j + 1 :
9062                         i == Xamoeba_1 ? 0 :
9063                         i == Xamoeba_2 ? 1 :
9064                         i == Xamoeba_3 ? 2 :
9065                         i == Xamoeba_4 ? 3 :
9066                         i == Xamoeba_5 ? 0 :
9067                         i == Xamoeba_6 ? 1 :
9068                         i == Xamoeba_7 ? 2 :
9069                         i == Xamoeba_8 ? 3 :
9070                         i == Xexit_2 ? j + 8 :
9071                         i == Xexit_3 ? j + 16 :
9072                         i == Xdynamite_1 ? 0 :
9073                         i == Xdynamite_2 ? 8 :
9074                         i == Xdynamite_3 ? 16 :
9075                         i == Xdynamite_4 ? 24 :
9076                         i == Xsand_stonein_1 ? j + 1 :
9077                         i == Xsand_stonein_2 ? j + 9 :
9078                         i == Xsand_stonein_3 ? j + 17 :
9079                         i == Xsand_stonein_4 ? j + 25 :
9080                         i == Xsand_stoneout_1 && j == 0 ? 0 :
9081                         i == Xsand_stoneout_1 && j == 1 ? 0 :
9082                         i == Xsand_stoneout_1 && j == 2 ? 1 :
9083                         i == Xsand_stoneout_1 && j == 3 ? 2 :
9084                         i == Xsand_stoneout_1 && j == 4 ? 2 :
9085                         i == Xsand_stoneout_1 && j == 5 ? 3 :
9086                         i == Xsand_stoneout_1 && j == 6 ? 4 :
9087                         i == Xsand_stoneout_1 && j == 7 ? 4 :
9088                         i == Xsand_stoneout_2 && j == 0 ? 5 :
9089                         i == Xsand_stoneout_2 && j == 1 ? 6 :
9090                         i == Xsand_stoneout_2 && j == 2 ? 7 :
9091                         i == Xsand_stoneout_2 && j == 3 ? 8 :
9092                         i == Xsand_stoneout_2 && j == 4 ? 9 :
9093                         i == Xsand_stoneout_2 && j == 5 ? 11 :
9094                         i == Xsand_stoneout_2 && j == 6 ? 13 :
9095                         i == Xsand_stoneout_2 && j == 7 ? 15 :
9096                         i == Xboom_bug && j == 1 ? 2 :
9097                         i == Xboom_bug && j == 2 ? 2 :
9098                         i == Xboom_bug && j == 3 ? 4 :
9099                         i == Xboom_bug && j == 4 ? 4 :
9100                         i == Xboom_bug && j == 5 ? 2 :
9101                         i == Xboom_bug && j == 6 ? 2 :
9102                         i == Xboom_bug && j == 7 ? 0 :
9103                         i == Xboom_tank && j == 1 ? 2 :
9104                         i == Xboom_tank && j == 2 ? 2 :
9105                         i == Xboom_tank && j == 3 ? 4 :
9106                         i == Xboom_tank && j == 4 ? 4 :
9107                         i == Xboom_tank && j == 5 ? 2 :
9108                         i == Xboom_tank && j == 6 ? 2 :
9109                         i == Xboom_tank && j == 7 ? 0 :
9110                         i == Xboom_android && j == 7 ? 6 :
9111                         i == Xboom_1 && j == 1 ? 2 :
9112                         i == Xboom_1 && j == 2 ? 2 :
9113                         i == Xboom_1 && j == 3 ? 4 :
9114                         i == Xboom_1 && j == 4 ? 4 :
9115                         i == Xboom_1 && j == 5 ? 6 :
9116                         i == Xboom_1 && j == 6 ? 6 :
9117                         i == Xboom_1 && j == 7 ? 8 :
9118                         i == Xboom_2 && j == 0 ? 8 :
9119                         i == Xboom_2 && j == 1 ? 8 :
9120                         i == Xboom_2 && j == 2 ? 10 :
9121                         i == Xboom_2 && j == 3 ? 10 :
9122                         i == Xboom_2 && j == 4 ? 10 :
9123                         i == Xboom_2 && j == 5 ? 12 :
9124                         i == Xboom_2 && j == 6 ? 12 :
9125                         i == Xboom_2 && j == 7 ? 12 :
9126                         special_animation && j == 4 ? 3 :
9127                         effective_action != action ? 0 :
9128                         j);
9129       int frame = getAnimationFrame(g->anim_frames,
9130                                     g->anim_delay,
9131                                     g->anim_mode,
9132                                     g->anim_start_frame,
9133                                     sync_frame);
9134
9135       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9136                           g->double_movement && is_backside);
9137
9138       g_em->bitmap = src_bitmap;
9139       g_em->src_x = src_x;
9140       g_em->src_y = src_y;
9141       g_em->src_offset_x = 0;
9142       g_em->src_offset_y = 0;
9143       g_em->dst_offset_x = 0;
9144       g_em->dst_offset_y = 0;
9145       g_em->width  = TILEX;
9146       g_em->height = TILEY;
9147
9148       g_em->preserve_background = FALSE;
9149
9150       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9151                                sync_frame);
9152
9153       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9154                                    effective_action == ACTION_MOVING  ||
9155                                    effective_action == ACTION_PUSHING ||
9156                                    effective_action == ACTION_EATING)) ||
9157           (!has_action_graphics && (effective_action == ACTION_FILLING ||
9158                                     effective_action == ACTION_EMPTYING)))
9159       {
9160         int move_dir =
9161           (effective_action == ACTION_FALLING ||
9162            effective_action == ACTION_FILLING ||
9163            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9164         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9165         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
9166         int num_steps = (i == Ydrip_1_s  ? 16 :
9167                          i == Ydrip_1_sB ? 16 :
9168                          i == Ydrip_2_s  ? 16 :
9169                          i == Ydrip_2_sB ? 16 :
9170                          i == Xsand_stonein_1 ? 32 :
9171                          i == Xsand_stonein_2 ? 32 :
9172                          i == Xsand_stonein_3 ? 32 :
9173                          i == Xsand_stonein_4 ? 32 :
9174                          i == Xsand_stoneout_1 ? 16 :
9175                          i == Xsand_stoneout_2 ? 16 : 8);
9176         int cx = ABS(dx) * (TILEX / num_steps);
9177         int cy = ABS(dy) * (TILEY / num_steps);
9178         int step_frame = (i == Ydrip_2_s        ? j + 8 :
9179                           i == Ydrip_2_sB       ? j + 8 :
9180                           i == Xsand_stonein_2  ? j + 8 :
9181                           i == Xsand_stonein_3  ? j + 16 :
9182                           i == Xsand_stonein_4  ? j + 24 :
9183                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9184         int step = (is_backside ? step_frame : num_steps - step_frame);
9185
9186         if (is_backside)        // tile where movement starts
9187         {
9188           if (dx < 0 || dy < 0)
9189           {
9190             g_em->src_offset_x = cx * step;
9191             g_em->src_offset_y = cy * step;
9192           }
9193           else
9194           {
9195             g_em->dst_offset_x = cx * step;
9196             g_em->dst_offset_y = cy * step;
9197           }
9198         }
9199         else                    // tile where movement ends
9200         {
9201           if (dx < 0 || dy < 0)
9202           {
9203             g_em->dst_offset_x = cx * step;
9204             g_em->dst_offset_y = cy * step;
9205           }
9206           else
9207           {
9208             g_em->src_offset_x = cx * step;
9209             g_em->src_offset_y = cy * step;
9210           }
9211         }
9212
9213         g_em->width  = TILEX - cx * step;
9214         g_em->height = TILEY - cy * step;
9215       }
9216
9217       // create unique graphic identifier to decide if tile must be redrawn
9218       /* bit 31 - 16 (16 bit): EM style graphic
9219          bit 15 - 12 ( 4 bit): EM style frame
9220          bit 11 -  6 ( 6 bit): graphic width
9221          bit  5 -  0 ( 6 bit): graphic height */
9222       g_em->unique_identifier =
9223         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9224     }
9225   }
9226
9227   for (i = 0; i < GAME_TILE_MAX; i++)
9228   {
9229     for (j = 0; j < 8; j++)
9230     {
9231       int element = object_mapping[i].element_rnd;
9232       int action = object_mapping[i].action;
9233       int direction = object_mapping[i].direction;
9234       boolean is_backside = object_mapping[i].is_backside;
9235       int graphic_action  = el_act_dir2img(element, action, direction);
9236       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9237
9238       if ((action == ACTION_SMASHED_BY_ROCK ||
9239            action == ACTION_SMASHED_BY_SPRING ||
9240            action == ACTION_EATING) &&
9241           graphic_action == graphic_default)
9242       {
9243         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
9244                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9245                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
9246                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9247                  Xspring);
9248
9249         // no separate animation for "smashed by rock" -- use rock instead
9250         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9251         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9252
9253         g_em->bitmap            = g_xx->bitmap;
9254         g_em->src_x             = g_xx->src_x;
9255         g_em->src_y             = g_xx->src_y;
9256         g_em->src_offset_x      = g_xx->src_offset_x;
9257         g_em->src_offset_y      = g_xx->src_offset_y;
9258         g_em->dst_offset_x      = g_xx->dst_offset_x;
9259         g_em->dst_offset_y      = g_xx->dst_offset_y;
9260         g_em->width             = g_xx->width;
9261         g_em->height            = g_xx->height;
9262         g_em->unique_identifier = g_xx->unique_identifier;
9263
9264         if (!is_backside)
9265           g_em->preserve_background = TRUE;
9266       }
9267     }
9268   }
9269
9270   for (p = 0; p < MAX_PLAYERS; p++)
9271   {
9272     for (i = 0; i < PLY_MAX; i++)
9273     {
9274       int element = player_mapping[p][i].element_rnd;
9275       int action = player_mapping[p][i].action;
9276       int direction = player_mapping[p][i].direction;
9277
9278       for (j = 0; j < 8; j++)
9279       {
9280         int effective_element = element;
9281         int effective_action = action;
9282         int graphic = (direction == MV_NONE ?
9283                        el_act2img(effective_element, effective_action) :
9284                        el_act_dir2img(effective_element, effective_action,
9285                                       direction));
9286         struct GraphicInfo *g = &graphic_info[graphic];
9287         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9288         Bitmap *src_bitmap;
9289         int src_x, src_y;
9290         int sync_frame = j;
9291         int frame = getAnimationFrame(g->anim_frames,
9292                                       g->anim_delay,
9293                                       g->anim_mode,
9294                                       g->anim_start_frame,
9295                                       sync_frame);
9296
9297         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9298
9299         g_em->bitmap = src_bitmap;
9300         g_em->src_x = src_x;
9301         g_em->src_y = src_y;
9302         g_em->src_offset_x = 0;
9303         g_em->src_offset_y = 0;
9304         g_em->dst_offset_x = 0;
9305         g_em->dst_offset_y = 0;
9306         g_em->width  = TILEX;
9307         g_em->height = TILEY;
9308       }
9309     }
9310   }
9311 }
9312
9313 static void CheckSaveEngineSnapshot_EM(int frame,
9314                                        boolean any_player_moving,
9315                                        boolean any_player_snapping,
9316                                        boolean any_player_dropping)
9317 {
9318   if (frame == 7 && !any_player_dropping)
9319   {
9320     if (!local_player->was_waiting)
9321     {
9322       if (!CheckSaveEngineSnapshotToList())
9323         return;
9324
9325       local_player->was_waiting = TRUE;
9326     }
9327   }
9328   else if (any_player_moving || any_player_snapping || any_player_dropping)
9329   {
9330     local_player->was_waiting = FALSE;
9331   }
9332 }
9333
9334 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9335                                        boolean murphy_is_dropping)
9336 {
9337   if (murphy_is_waiting)
9338   {
9339     if (!local_player->was_waiting)
9340     {
9341       if (!CheckSaveEngineSnapshotToList())
9342         return;
9343
9344       local_player->was_waiting = TRUE;
9345     }
9346   }
9347   else
9348   {
9349     local_player->was_waiting = FALSE;
9350   }
9351 }
9352
9353 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9354                                        boolean button_released)
9355 {
9356   if (button_released)
9357   {
9358     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9359       CheckSaveEngineSnapshotToList();
9360   }
9361   else if (element_clicked)
9362   {
9363     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9364       CheckSaveEngineSnapshotToList();
9365
9366     game.snapshot.changed_action = TRUE;
9367   }
9368 }
9369
9370 boolean CheckSingleStepMode_EM(int frame,
9371                                boolean any_player_moving,
9372                                boolean any_player_snapping,
9373                                boolean any_player_dropping)
9374 {
9375   if (tape.single_step && tape.recording && !tape.pausing)
9376     if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9377       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9378
9379   CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9380                              any_player_snapping, any_player_dropping);
9381
9382   return tape.pausing;
9383 }
9384
9385 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9386                             boolean murphy_is_dropping)
9387 {
9388   boolean murphy_starts_dropping = FALSE;
9389   int i;
9390
9391   for (i = 0; i < MAX_PLAYERS; i++)
9392     if (stored_player[i].force_dropping)
9393       murphy_starts_dropping = TRUE;
9394
9395   if (tape.single_step && tape.recording && !tape.pausing)
9396     if (murphy_is_waiting && !murphy_starts_dropping)
9397       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9398
9399   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9400 }
9401
9402 void CheckSingleStepMode_MM(boolean element_clicked,
9403                             boolean button_released)
9404 {
9405   if (tape.single_step && tape.recording && !tape.pausing)
9406     if (button_released)
9407       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9408
9409   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9410 }
9411
9412 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9413                          int graphic, int sync_frame)
9414 {
9415   int frame = getGraphicAnimationFrame(graphic, sync_frame);
9416
9417   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9418 }
9419
9420 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9421 {
9422   return (IS_NEXT_FRAME(sync_frame, graphic));
9423 }
9424
9425 int getGraphicInfo_Delay(int graphic)
9426 {
9427   return graphic_info[graphic].anim_delay;
9428 }
9429
9430 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9431 {
9432   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9433     return FALSE;
9434
9435   if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9436     return FALSE;
9437
9438   return TRUE;
9439 }
9440
9441 void PlayMenuSoundExt(int sound)
9442 {
9443   if (sound == SND_UNDEFINED)
9444     return;
9445
9446   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9447       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9448     return;
9449
9450   if (IS_LOOP_SOUND(sound))
9451     PlaySoundLoop(sound);
9452   else
9453     PlaySound(sound);
9454 }
9455
9456 void PlayMenuSound(void)
9457 {
9458   PlayMenuSoundExt(menu.sound[game_status]);
9459 }
9460
9461 void PlayMenuSoundStereo(int sound, int stereo_position)
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     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9472   else
9473     PlaySoundStereo(sound, stereo_position);
9474 }
9475
9476 void PlayMenuSoundIfLoopExt(int sound)
9477 {
9478   if (sound == SND_UNDEFINED)
9479     return;
9480
9481   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9482       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9483     return;
9484
9485   if (IS_LOOP_SOUND(sound))
9486     PlaySoundLoop(sound);
9487 }
9488
9489 void PlayMenuSoundIfLoop(void)
9490 {
9491   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9492 }
9493
9494 void PlayMenuMusicExt(int music)
9495 {
9496   if (music == MUS_UNDEFINED)
9497     return;
9498
9499   if (!setup.sound_music)
9500     return;
9501
9502   if (IS_LOOP_MUSIC(music))
9503     PlayMusicLoop(music);
9504   else
9505     PlayMusic(music);
9506 }
9507
9508 void PlayMenuMusic(void)
9509 {
9510   char *curr_music = getCurrentlyPlayingMusicFilename();
9511   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9512
9513   if (!strEqual(curr_music, next_music))
9514     PlayMenuMusicExt(menu.music[game_status]);
9515 }
9516
9517 void PlayMenuSoundsAndMusic(void)
9518 {
9519   PlayMenuSound();
9520   PlayMenuMusic();
9521 }
9522
9523 static void FadeMenuSounds(void)
9524 {
9525   FadeSounds();
9526 }
9527
9528 static void FadeMenuMusic(void)
9529 {
9530   char *curr_music = getCurrentlyPlayingMusicFilename();
9531   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9532
9533   if (!strEqual(curr_music, next_music))
9534     FadeMusic();
9535 }
9536
9537 void FadeMenuSoundsAndMusic(void)
9538 {
9539   FadeMenuSounds();
9540   FadeMenuMusic();
9541 }
9542
9543 void PlaySoundActivating(void)
9544 {
9545 #if 0
9546   PlaySound(SND_MENU_ITEM_ACTIVATING);
9547 #endif
9548 }
9549
9550 void PlaySoundSelecting(void)
9551 {
9552 #if 0
9553   PlaySound(SND_MENU_ITEM_SELECTING);
9554 #endif
9555 }
9556
9557 void ToggleFullscreenIfNeeded(void)
9558 {
9559   // if setup and video fullscreen state are already matching, nothing do do
9560   if (setup.fullscreen == video.fullscreen_enabled ||
9561       !video.fullscreen_available)
9562     return;
9563
9564   SDLSetWindowFullscreen(setup.fullscreen);
9565
9566   // set setup value according to successfully changed fullscreen mode
9567   setup.fullscreen = video.fullscreen_enabled;
9568 }
9569
9570 void ChangeWindowScalingIfNeeded(void)
9571 {
9572   // if setup and video window scaling are already matching, nothing do do
9573   if (setup.window_scaling_percent == video.window_scaling_percent ||
9574       video.fullscreen_enabled)
9575     return;
9576
9577   SDLSetWindowScaling(setup.window_scaling_percent);
9578
9579   // set setup value according to successfully changed window scaling
9580   setup.window_scaling_percent = video.window_scaling_percent;
9581 }
9582
9583 void ChangeVsyncModeIfNeeded(void)
9584 {
9585   int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9586   int video_vsync_mode = video.vsync_mode;
9587
9588   // if setup and video vsync mode are already matching, nothing do do
9589   if (setup_vsync_mode == video_vsync_mode)
9590     return;
9591
9592   // if renderer is using OpenGL, vsync mode can directly be changed
9593   SDLSetScreenVsyncMode(setup.vsync_mode);
9594
9595   // if vsync mode unchanged, try re-creating renderer to set vsync mode
9596   if (video.vsync_mode == video_vsync_mode)
9597   {
9598     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9599
9600     // save backbuffer content which gets lost when re-creating screen
9601     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9602
9603     // force re-creating screen and renderer to set new vsync mode
9604     video.fullscreen_enabled = !setup.fullscreen;
9605
9606     // when creating new renderer, destroy textures linked to old renderer
9607     FreeAllImageTextures();     // needs old renderer to free the textures
9608
9609     // re-create screen and renderer (including change of vsync mode)
9610     ChangeVideoModeIfNeeded(setup.fullscreen);
9611
9612     // set setup value according to successfully changed fullscreen mode
9613     setup.fullscreen = video.fullscreen_enabled;
9614
9615     // restore backbuffer content from temporary backbuffer backup bitmap
9616     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9617     FreeBitmap(tmp_backbuffer);
9618
9619     // update visible window/screen
9620     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9621
9622     // when changing vsync mode, re-create textures for new renderer
9623     InitImageTextures();
9624   }
9625
9626   // set setup value according to successfully changed vsync mode
9627   setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9628 }
9629
9630 static void JoinRectangles(int *x, int *y, int *width, int *height,
9631                            int x2, int y2, int width2, int height2)
9632 {
9633   // do not join with "off-screen" rectangle
9634   if (x2 == -1 || y2 == -1)
9635     return;
9636
9637   *x = MIN(*x, x2);
9638   *y = MIN(*y, y2);
9639   *width = MAX(*width, width2);
9640   *height = MAX(*height, height2);
9641 }
9642
9643 void SetAnimStatus(int anim_status_new)
9644 {
9645   if (anim_status_new == GAME_MODE_MAIN)
9646     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9647   else if (anim_status_new == GAME_MODE_NAMES)
9648     anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9649   else if (anim_status_new == GAME_MODE_SCORES)
9650     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9651
9652   global.anim_status_next = anim_status_new;
9653
9654   // directly set screen modes that are entered without fading
9655   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
9656        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9657       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
9658        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9659       (global.anim_status      == GAME_MODE_PSEUDO_NAMESONLY &&
9660        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9661       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAMES &&
9662        global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9663     global.anim_status = global.anim_status_next;
9664 }
9665
9666 void SetGameStatus(int game_status_new)
9667 {
9668   if (game_status_new != game_status)
9669     game_status_last_screen = game_status;
9670
9671   game_status = game_status_new;
9672
9673   SetAnimStatus(game_status_new);
9674 }
9675
9676 void SetFontStatus(int game_status_new)
9677 {
9678   static int last_game_status = -1;
9679
9680   if (game_status_new != -1)
9681   {
9682     // set game status for font use after storing last game status
9683     last_game_status = game_status;
9684     game_status = game_status_new;
9685   }
9686   else
9687   {
9688     // reset game status after font use from last stored game status
9689     game_status = last_game_status;
9690   }
9691 }
9692
9693 void ResetFontStatus(void)
9694 {
9695   SetFontStatus(-1);
9696 }
9697
9698 void SetLevelSetInfo(char *identifier, int level_nr)
9699 {
9700   setString(&levelset.identifier, identifier);
9701
9702   levelset.level_nr = level_nr;
9703 }
9704
9705 boolean CheckIfAllViewportsHaveChanged(void)
9706 {
9707   // if game status has not changed, viewports have not changed either
9708   if (game_status == game_status_last)
9709     return FALSE;
9710
9711   // check if all viewports have changed with current game status
9712
9713   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9714   struct RectWithBorder *vp_door_1    = &viewport.door_1[game_status];
9715   struct RectWithBorder *vp_door_2    = &viewport.door_2[game_status];
9716   int new_real_sx       = vp_playfield->x;
9717   int new_real_sy       = vp_playfield->y;
9718   int new_full_sxsize   = vp_playfield->width;
9719   int new_full_sysize   = vp_playfield->height;
9720   int new_dx            = vp_door_1->x;
9721   int new_dy            = vp_door_1->y;
9722   int new_dxsize        = vp_door_1->width;
9723   int new_dysize        = vp_door_1->height;
9724   int new_vx            = vp_door_2->x;
9725   int new_vy            = vp_door_2->y;
9726   int new_vxsize        = vp_door_2->width;
9727   int new_vysize        = vp_door_2->height;
9728
9729   boolean playfield_viewport_has_changed =
9730     (new_real_sx != REAL_SX ||
9731      new_real_sy != REAL_SY ||
9732      new_full_sxsize != FULL_SXSIZE ||
9733      new_full_sysize != FULL_SYSIZE);
9734
9735   boolean door_1_viewport_has_changed =
9736     (new_dx != DX ||
9737      new_dy != DY ||
9738      new_dxsize != DXSIZE ||
9739      new_dysize != DYSIZE);
9740
9741   boolean door_2_viewport_has_changed =
9742     (new_vx != VX ||
9743      new_vy != VY ||
9744      new_vxsize != VXSIZE ||
9745      new_vysize != VYSIZE ||
9746      game_status_last == GAME_MODE_EDITOR);
9747
9748   return (playfield_viewport_has_changed &&
9749           door_1_viewport_has_changed &&
9750           door_2_viewport_has_changed);
9751 }
9752
9753 boolean CheckFadeAll(void)
9754 {
9755   return (CheckIfGlobalBorderHasChanged() ||
9756           CheckIfAllViewportsHaveChanged());
9757 }
9758
9759 void ChangeViewportPropertiesIfNeeded(void)
9760 {
9761   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9762                                FALSE : setup.small_game_graphics);
9763   int gfx_game_mode = getGlobalGameStatus(game_status);
9764   int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9765                         gfx_game_mode);
9766   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
9767   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9768   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
9769   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
9770   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
9771   int new_win_xsize     = vp_window->width;
9772   int new_win_ysize     = vp_window->height;
9773   int border_left       = vp_playfield->border_left;
9774   int border_right      = vp_playfield->border_right;
9775   int border_top        = vp_playfield->border_top;
9776   int border_bottom     = vp_playfield->border_bottom;
9777   int new_sx            = vp_playfield->x      + border_left;
9778   int new_sy            = vp_playfield->y      + border_top;
9779   int new_sxsize        = vp_playfield->width  - border_left - border_right;
9780   int new_sysize        = vp_playfield->height - border_top  - border_bottom;
9781   int new_real_sx       = vp_playfield->x;
9782   int new_real_sy       = vp_playfield->y;
9783   int new_full_sxsize   = vp_playfield->width;
9784   int new_full_sysize   = vp_playfield->height;
9785   int new_dx            = vp_door_1->x;
9786   int new_dy            = vp_door_1->y;
9787   int new_dxsize        = vp_door_1->width;
9788   int new_dysize        = vp_door_1->height;
9789   int new_vx            = vp_door_2->x;
9790   int new_vy            = vp_door_2->y;
9791   int new_vxsize        = vp_door_2->width;
9792   int new_vysize        = vp_door_2->height;
9793   int new_ex            = vp_door_3->x;
9794   int new_ey            = vp_door_3->y;
9795   int new_exsize        = vp_door_3->width;
9796   int new_eysize        = vp_door_3->height;
9797   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9798   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9799                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9800   int new_scr_fieldx = new_sxsize / tilesize;
9801   int new_scr_fieldy = new_sysize / tilesize;
9802   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9803   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9804   boolean init_gfx_buffers = FALSE;
9805   boolean init_video_buffer = FALSE;
9806   boolean init_gadgets_and_anims = FALSE;
9807   boolean init_em_graphics = FALSE;
9808
9809   if (new_win_xsize != WIN_XSIZE ||
9810       new_win_ysize != WIN_YSIZE)
9811   {
9812     WIN_XSIZE = new_win_xsize;
9813     WIN_YSIZE = new_win_ysize;
9814
9815     init_video_buffer = TRUE;
9816     init_gfx_buffers = TRUE;
9817     init_gadgets_and_anims = TRUE;
9818
9819     // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9820   }
9821
9822   if (new_scr_fieldx != SCR_FIELDX ||
9823       new_scr_fieldy != SCR_FIELDY)
9824   {
9825     // this always toggles between MAIN and GAME when using small tile size
9826
9827     SCR_FIELDX = new_scr_fieldx;
9828     SCR_FIELDY = new_scr_fieldy;
9829
9830     // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9831   }
9832
9833   if (new_sx != SX ||
9834       new_sy != SY ||
9835       new_dx != DX ||
9836       new_dy != DY ||
9837       new_vx != VX ||
9838       new_vy != VY ||
9839       new_ex != EX ||
9840       new_ey != EY ||
9841       new_sxsize != SXSIZE ||
9842       new_sysize != SYSIZE ||
9843       new_dxsize != DXSIZE ||
9844       new_dysize != DYSIZE ||
9845       new_vxsize != VXSIZE ||
9846       new_vysize != VYSIZE ||
9847       new_exsize != EXSIZE ||
9848       new_eysize != EYSIZE ||
9849       new_real_sx != REAL_SX ||
9850       new_real_sy != REAL_SY ||
9851       new_full_sxsize != FULL_SXSIZE ||
9852       new_full_sysize != FULL_SYSIZE ||
9853       new_tilesize_var != TILESIZE_VAR
9854       )
9855   {
9856     // ------------------------------------------------------------------------
9857     // determine next fading area for changed viewport definitions
9858     // ------------------------------------------------------------------------
9859
9860     // start with current playfield area (default fading area)
9861     FADE_SX = REAL_SX;
9862     FADE_SY = REAL_SY;
9863     FADE_SXSIZE = FULL_SXSIZE;
9864     FADE_SYSIZE = FULL_SYSIZE;
9865
9866     // add new playfield area if position or size has changed
9867     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9868         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9869     {
9870       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9871                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9872     }
9873
9874     // add current and new door 1 area if position or size has changed
9875     if (new_dx != DX || new_dy != DY ||
9876         new_dxsize != DXSIZE || new_dysize != DYSIZE)
9877     {
9878       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9879                      DX, DY, DXSIZE, DYSIZE);
9880       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9881                      new_dx, new_dy, new_dxsize, new_dysize);
9882     }
9883
9884     // add current and new door 2 area if position or size has changed
9885     if (new_vx != VX || new_vy != VY ||
9886         new_vxsize != VXSIZE || new_vysize != VYSIZE)
9887     {
9888       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9889                      VX, VY, VXSIZE, VYSIZE);
9890       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9891                      new_vx, new_vy, new_vxsize, new_vysize);
9892     }
9893
9894     // ------------------------------------------------------------------------
9895     // handle changed tile size
9896     // ------------------------------------------------------------------------
9897
9898     if (new_tilesize_var != TILESIZE_VAR)
9899     {
9900       // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9901
9902       // changing tile size invalidates scroll values of engine snapshots
9903       FreeEngineSnapshotSingle();
9904
9905       // changing tile size requires update of graphic mapping for EM engine
9906       init_em_graphics = TRUE;
9907     }
9908
9909     SX = new_sx;
9910     SY = new_sy;
9911     DX = new_dx;
9912     DY = new_dy;
9913     VX = new_vx;
9914     VY = new_vy;
9915     EX = new_ex;
9916     EY = new_ey;
9917     SXSIZE = new_sxsize;
9918     SYSIZE = new_sysize;
9919     DXSIZE = new_dxsize;
9920     DYSIZE = new_dysize;
9921     VXSIZE = new_vxsize;
9922     VYSIZE = new_vysize;
9923     EXSIZE = new_exsize;
9924     EYSIZE = new_eysize;
9925     REAL_SX = new_real_sx;
9926     REAL_SY = new_real_sy;
9927     FULL_SXSIZE = new_full_sxsize;
9928     FULL_SYSIZE = new_full_sysize;
9929     TILESIZE_VAR = new_tilesize_var;
9930
9931     init_gfx_buffers = TRUE;
9932     init_gadgets_and_anims = TRUE;
9933
9934     // Debug("tools:viewport", "viewports: init_gfx_buffers");
9935     // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9936   }
9937
9938   if (init_gfx_buffers)
9939   {
9940     // Debug("tools:viewport", "init_gfx_buffers");
9941
9942     SCR_FIELDX = new_scr_fieldx_buffers;
9943     SCR_FIELDY = new_scr_fieldy_buffers;
9944
9945     InitGfxBuffers();
9946
9947     SCR_FIELDX = new_scr_fieldx;
9948     SCR_FIELDY = new_scr_fieldy;
9949
9950     SetDrawDeactivationMask(REDRAW_NONE);
9951     SetDrawBackgroundMask(REDRAW_FIELD);
9952   }
9953
9954   if (init_video_buffer)
9955   {
9956     // Debug("tools:viewport", "init_video_buffer");
9957
9958     FreeAllImageTextures();     // needs old renderer to free the textures
9959
9960     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9961     InitImageTextures();
9962   }
9963
9964   if (init_gadgets_and_anims)
9965   {
9966     // Debug("tools:viewport", "init_gadgets_and_anims");
9967
9968     InitGadgets();
9969     InitGlobalAnimations();
9970   }
9971
9972   if (init_em_graphics)
9973   {
9974     InitGraphicInfo_EM();
9975   }
9976 }
9977
9978 void OpenURL(char *url)
9979 {
9980 #if SDL_VERSION_ATLEAST(2,0,14)
9981   SDL_OpenURL(url);
9982 #else
9983   Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9984        url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9985   Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9986 #endif
9987 }
9988
9989 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9990 {
9991   OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9992 }
9993
9994
9995 // ============================================================================
9996 // tests
9997 // ============================================================================
9998
9999 #if defined(PLATFORM_WINDOWS)
10000 /* FILETIME of Jan 1 1970 00:00:00. */
10001 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10002
10003 /*
10004  * timezone information is stored outside the kernel so tzp isn't used anymore.
10005  *
10006  * Note: this function is not for Win32 high precision timing purpose. See
10007  * elapsed_time().
10008  */
10009 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10010 {
10011   FILETIME    file_time;
10012   SYSTEMTIME  system_time;
10013   ULARGE_INTEGER ularge;
10014
10015   GetSystemTime(&system_time);
10016   SystemTimeToFileTime(&system_time, &file_time);
10017   ularge.LowPart = file_time.dwLowDateTime;
10018   ularge.HighPart = file_time.dwHighDateTime;
10019
10020   tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10021   tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10022
10023   return 0;
10024 }
10025 #endif
10026
10027 static char *test_init_uuid_random_function_simple(void)
10028 {
10029   static char seed_text[100];
10030   unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10031
10032   sprintf(seed_text, "%d", seed);
10033
10034   return seed_text;
10035 }
10036
10037 static char *test_init_uuid_random_function_better(void)
10038 {
10039   static char seed_text[100];
10040   struct timeval current_time;
10041
10042   gettimeofday(&current_time, NULL);
10043
10044   prng_seed_bytes(&current_time, sizeof(current_time));
10045
10046   sprintf(seed_text, "%ld.%ld",
10047           (long)current_time.tv_sec,
10048           (long)current_time.tv_usec);
10049
10050   return seed_text;
10051 }
10052
10053 #if defined(PLATFORM_WINDOWS)
10054 static char *test_init_uuid_random_function_better_windows(void)
10055 {
10056   static char seed_text[100];
10057   struct timeval current_time;
10058
10059   gettimeofday_windows(&current_time, NULL);
10060
10061   prng_seed_bytes(&current_time, sizeof(current_time));
10062
10063   sprintf(seed_text, "%ld.%ld",
10064           (long)current_time.tv_sec,
10065           (long)current_time.tv_usec);
10066
10067   return seed_text;
10068 }
10069 #endif
10070
10071 static unsigned int test_uuid_random_function_simple(int max)
10072 {
10073   return GetSimpleRandom(max);
10074 }
10075
10076 static unsigned int test_uuid_random_function_better(int max)
10077 {
10078   return (max > 0 ? prng_get_uint() % max : 0);
10079 }
10080
10081 #if defined(PLATFORM_WINDOWS)
10082 #define NUM_UUID_TESTS                  3
10083 #else
10084 #define NUM_UUID_TESTS                  2
10085 #endif
10086
10087 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10088 {
10089   struct hashtable *hash_seeds =
10090     create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10091   struct hashtable *hash_uuids =
10092     create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10093   static char message[100];
10094   int i;
10095
10096   char *random_name = (nr == 0 ? "simple" : "better");
10097   char *random_type = (always_seed ? "always" : "only once");
10098   char *(*init_random_function)(void) =
10099     (nr == 0 ?
10100      test_init_uuid_random_function_simple :
10101      test_init_uuid_random_function_better);
10102   unsigned int (*random_function)(int) =
10103     (nr == 0 ?
10104      test_uuid_random_function_simple :
10105      test_uuid_random_function_better);
10106   int xpos = 40;
10107
10108 #if defined(PLATFORM_WINDOWS)
10109   if (nr == 2)
10110   {
10111     random_name = "windows";
10112     init_random_function = test_init_uuid_random_function_better_windows;
10113   }
10114 #endif
10115
10116   ClearField();
10117
10118   DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10119   DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10120
10121   DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10122   DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10123   DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10124
10125   DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10126
10127   BackToFront();
10128
10129   // always initialize random number generator at least once
10130   init_random_function();
10131
10132   unsigned int time_start = SDL_GetTicks();
10133
10134   for (i = 0; i < num_uuids; i++)
10135   {
10136     if (always_seed)
10137     {
10138       char *seed = getStringCopy(init_random_function());
10139
10140       hashtable_remove(hash_seeds, seed);
10141       hashtable_insert(hash_seeds, seed, "1");
10142     }
10143
10144     char *uuid = getStringCopy(getUUIDExt(random_function));
10145
10146     hashtable_remove(hash_uuids, uuid);
10147     hashtable_insert(hash_uuids, uuid, "1");
10148   }
10149
10150   int num_unique_seeds = hashtable_count(hash_seeds);
10151   int num_unique_uuids = hashtable_count(hash_uuids);
10152
10153   unsigned int time_needed = SDL_GetTicks() - time_start;
10154
10155   DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10156
10157   DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10158
10159   if (always_seed)
10160     DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10161
10162   if (nr == NUM_UUID_TESTS - 1 && always_seed)
10163     DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10164   else
10165     DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10166
10167   sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10168
10169   Request(message, REQ_CONFIRM);
10170
10171   hashtable_destroy(hash_seeds, 0);
10172   hashtable_destroy(hash_uuids, 0);
10173 }
10174
10175 void TestGeneratingUUIDs(void)
10176 {
10177   int num_uuids = 1000000;
10178   int i, j;
10179
10180   for (i = 0; i < NUM_UUID_TESTS; i++)
10181     for (j = 0; j < 2; j++)
10182       TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10183
10184   CloseAllAndExit(0);
10185 }