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