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