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