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