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