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