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