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