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