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