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