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