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