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