added variable to store if request dialog event was processed
[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     boolean event_handled = FALSE;
4242
4243     if (game_just_ended)
4244     {
4245       SetDrawtoField(draw_buffer_last);
4246
4247       HandleGameActions();
4248
4249       SetDrawtoField(DRAW_TO_BACKBUFFER);
4250
4251       if (global.use_envelope_request)
4252       {
4253         // copy current state of request area to middle of playfield area
4254         BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4255       }
4256     }
4257
4258     if (PendingEvent())
4259     {
4260       Event event;
4261
4262       while (NextValidEvent(&event))
4263       {
4264         event_handled = TRUE;
4265
4266         switch (event.type)
4267         {
4268           case EVENT_BUTTONPRESS:
4269           case EVENT_BUTTONRELEASE:
4270           case EVENT_MOTIONNOTIFY:
4271           {
4272             int mx, my;
4273
4274             if (event.type == EVENT_MOTIONNOTIFY)
4275             {
4276               if (!button_status)
4277                 continue;
4278
4279               motion_status = TRUE;
4280               mx = ((MotionEvent *) &event)->x;
4281               my = ((MotionEvent *) &event)->y;
4282             }
4283             else
4284             {
4285               motion_status = FALSE;
4286               mx = ((ButtonEvent *) &event)->x;
4287               my = ((ButtonEvent *) &event)->y;
4288               if (event.type == EVENT_BUTTONPRESS)
4289                 button_status = ((ButtonEvent *) &event)->button;
4290               else
4291                 button_status = MB_RELEASED;
4292             }
4293
4294             // this sets 'request_gadget_id'
4295             HandleGadgets(mx, my, button_status);
4296
4297             switch (request_gadget_id)
4298             {
4299               case TOOL_CTRL_ID_YES:
4300               case TOOL_CTRL_ID_TOUCH_YES:
4301                 result = TRUE;
4302                 break;
4303               case TOOL_CTRL_ID_NO:
4304               case TOOL_CTRL_ID_TOUCH_NO:
4305                 result = FALSE;
4306                 break;
4307               case TOOL_CTRL_ID_CONFIRM:
4308               case TOOL_CTRL_ID_TOUCH_CONFIRM:
4309                 result = TRUE | FALSE;
4310                 break;
4311
4312               case TOOL_CTRL_ID_PLAYER_1:
4313                 result = 1;
4314                 break;
4315               case TOOL_CTRL_ID_PLAYER_2:
4316                 result = 2;
4317                 break;
4318               case TOOL_CTRL_ID_PLAYER_3:
4319                 result = 3;
4320                 break;
4321               case TOOL_CTRL_ID_PLAYER_4:
4322                 result = 4;
4323                 break;
4324
4325               default:
4326                 // only check clickable animations if no request gadget clicked
4327                 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4328                 break;
4329             }
4330
4331             break;
4332           }
4333
4334           case SDL_WINDOWEVENT:
4335             HandleWindowEvent((WindowEvent *) &event);
4336             break;
4337
4338           case SDL_APP_WILLENTERBACKGROUND:
4339           case SDL_APP_DIDENTERBACKGROUND:
4340           case SDL_APP_WILLENTERFOREGROUND:
4341           case SDL_APP_DIDENTERFOREGROUND:
4342             HandlePauseResumeEvent((PauseResumeEvent *) &event);
4343             break;
4344
4345           case EVENT_KEYPRESS:
4346           {
4347             Key key = GetEventKey((KeyEvent *)&event, TRUE);
4348
4349             switch (key)
4350             {
4351               case KSYM_space:
4352                 if (req_state & REQ_CONFIRM)
4353                   result = 1;
4354                 break;
4355
4356               case KSYM_Return:
4357               case KSYM_y:
4358               case KSYM_Y:
4359               case KSYM_Select:
4360               case KSYM_Menu:
4361 #if defined(KSYM_Rewind)
4362               case KSYM_Rewind:         // for Amazon Fire TV remote
4363 #endif
4364                 result = 1;
4365                 break;
4366
4367               case KSYM_Escape:
4368               case KSYM_n:
4369               case KSYM_N:
4370               case KSYM_Back:
4371 #if defined(KSYM_FastForward)
4372               case KSYM_FastForward:    // for Amazon Fire TV remote
4373 #endif
4374                 result = 0;
4375                 break;
4376
4377               default:
4378                 HandleKeysDebug(key, KEY_PRESSED);
4379                 break;
4380             }
4381
4382             if (req_state & REQ_PLAYER)
4383             {
4384               int old_player_nr = setup.network_player_nr;
4385
4386               if (result != -1)
4387                 result = old_player_nr + 1;
4388
4389               switch (key)
4390               {
4391                 case KSYM_space:
4392                   result = old_player_nr + 1;
4393                   break;
4394
4395                 case KSYM_Up:
4396                 case KSYM_1:
4397                   result = 1;
4398                   break;
4399
4400                 case KSYM_Right:
4401                 case KSYM_2:
4402                   result = 2;
4403                   break;
4404
4405                 case KSYM_Down:
4406                 case KSYM_3:
4407                   result = 3;
4408                   break;
4409
4410                 case KSYM_Left:
4411                 case KSYM_4:
4412                   result = 4;
4413                   break;
4414
4415                 default:
4416                   break;
4417               }
4418             }
4419
4420             break;
4421           }
4422
4423           case EVENT_FINGERRELEASE:
4424           case EVENT_KEYRELEASE:
4425             ClearPlayerAction();
4426             break;
4427
4428           case SDL_CONTROLLERBUTTONDOWN:
4429             switch (event.cbutton.button)
4430             {
4431               case SDL_CONTROLLER_BUTTON_A:
4432               case SDL_CONTROLLER_BUTTON_X:
4433               case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4434               case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4435                 result = 1;
4436                 break;
4437
4438               case SDL_CONTROLLER_BUTTON_B:
4439               case SDL_CONTROLLER_BUTTON_Y:
4440               case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4441               case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4442               case SDL_CONTROLLER_BUTTON_BACK:
4443                 result = 0;
4444                 break;
4445             }
4446
4447             if (req_state & REQ_PLAYER)
4448             {
4449               int old_player_nr = setup.network_player_nr;
4450
4451               if (result != -1)
4452                 result = old_player_nr + 1;
4453
4454               switch (event.cbutton.button)
4455               {
4456                 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4457                 case SDL_CONTROLLER_BUTTON_Y:
4458                   result = 1;
4459                   break;
4460
4461                 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4462                 case SDL_CONTROLLER_BUTTON_B:
4463                   result = 2;
4464                   break;
4465
4466                 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4467                 case SDL_CONTROLLER_BUTTON_A:
4468                   result = 3;
4469                   break;
4470
4471                 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4472                 case SDL_CONTROLLER_BUTTON_X:
4473                   result = 4;
4474                   break;
4475
4476                 default:
4477                   break;
4478               }
4479             }
4480
4481             break;
4482
4483           case SDL_CONTROLLERBUTTONUP:
4484             HandleJoystickEvent(&event);
4485             ClearPlayerAction();
4486             break;
4487
4488           default:
4489             HandleOtherEvents(&event);
4490             break;
4491         }
4492       }
4493     }
4494     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4495     {
4496       int joy = AnyJoystick();
4497
4498       if (joy & JOY_BUTTON_1)
4499         result = 1;
4500       else if (joy & JOY_BUTTON_2)
4501         result = 0;
4502     }
4503     else if (AnyJoystick())
4504     {
4505       int joy = AnyJoystick();
4506
4507       if (req_state & REQ_PLAYER)
4508       {
4509         if (joy & JOY_UP)
4510           result = 1;
4511         else if (joy & JOY_RIGHT)
4512           result = 2;
4513         else if (joy & JOY_DOWN)
4514           result = 3;
4515         else if (joy & JOY_LEFT)
4516           result = 4;
4517       }
4518     }
4519
4520     if (event_handled)
4521     {
4522       if (game_just_ended)
4523       {
4524         if (global.use_envelope_request)
4525         {
4526           // copy back current state of pressed buttons inside request area
4527           BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4528         }
4529       }
4530     }
4531
4532     BackToFront();
4533   }
4534
4535   SetDrawtoField(draw_buffer_last);
4536
4537   game.request_active = FALSE;
4538
4539   return result;
4540 }
4541
4542 static boolean RequestDoor(char *text, unsigned int req_state)
4543 {
4544   unsigned int old_door_state;
4545   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4546   int font_nr = FONT_TEXT_2;
4547   char *text_ptr;
4548   int result;
4549   int ty;
4550
4551   if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4552   {
4553     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4554     font_nr = FONT_TEXT_1;
4555   }
4556
4557   if (game_status == GAME_MODE_PLAYING)
4558     BlitScreenToBitmap(backbuffer);
4559
4560   // disable deactivated drawing when quick-loading level tape recording
4561   if (tape.playing && tape.deactivate_display)
4562     TapeDeactivateDisplayOff(TRUE);
4563
4564   SetMouseCursor(CURSOR_DEFAULT);
4565
4566   // pause network game while waiting for request to answer
4567   if (network.enabled &&
4568       game_status == GAME_MODE_PLAYING &&
4569       !game.all_players_gone &&
4570       req_state & REQUEST_WAIT_FOR_INPUT)
4571     SendToServer_PausePlaying();
4572
4573   old_door_state = GetDoorState();
4574
4575   // simulate releasing mouse button over last gadget, if still pressed
4576   if (button_status)
4577     HandleGadgets(-1, -1, 0);
4578
4579   UnmapAllGadgets();
4580
4581   // draw released gadget before proceeding
4582   // BackToFront();
4583
4584   if (old_door_state & DOOR_OPEN_1)
4585   {
4586     CloseDoor(DOOR_CLOSE_1);
4587
4588     // save old door content
4589     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4590                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4591   }
4592
4593   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4594   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4595
4596   // clear door drawing field
4597   DrawBackground(DX, DY, DXSIZE, DYSIZE);
4598
4599   // force DOOR font inside door area
4600   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4601
4602   // write text for request
4603   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4604   {
4605     char text_line[max_request_line_len + 1];
4606     int tx, tl, tc = 0;
4607
4608     if (!*text_ptr)
4609       break;
4610
4611     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4612     {
4613       tc = *(text_ptr + tx);
4614       // if (!tc || tc == ' ')
4615       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4616         break;
4617     }
4618
4619     if ((tc == '?' || tc == '!') && tl == 0)
4620       tl = 1;
4621
4622     if (!tl)
4623     { 
4624       text_ptr++; 
4625       ty--; 
4626       continue; 
4627     }
4628
4629     strncpy(text_line, text_ptr, tl);
4630     text_line[tl] = 0;
4631
4632     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4633              DY + 8 + ty * (getFontHeight(font_nr) + 2),
4634              text_line, font_nr);
4635
4636     text_ptr += tl + (tc == ' ' ? 1 : 0);
4637     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4638   }
4639
4640   ResetFontStatus();
4641
4642   if (req_state & REQ_ASK)
4643   {
4644     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4645     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4646     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4647     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4648   }
4649   else if (req_state & REQ_CONFIRM)
4650   {
4651     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4652     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4653   }
4654   else if (req_state & REQ_PLAYER)
4655   {
4656     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4657     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4658     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4659     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4660   }
4661
4662   // copy request gadgets to door backbuffer
4663   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4664
4665   OpenDoor(DOOR_OPEN_1);
4666
4667   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4668   {
4669     if (game_status == GAME_MODE_PLAYING)
4670     {
4671       SetPanelBackground();
4672       SetDrawBackgroundMask(REDRAW_DOOR_1);
4673     }
4674     else
4675     {
4676       SetDrawBackgroundMask(REDRAW_FIELD);
4677     }
4678
4679     return FALSE;
4680   }
4681
4682   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4683
4684   // ---------- handle request buttons ----------
4685   result = RequestHandleEvents(req_state);
4686
4687   UnmapToolButtons();
4688
4689   if (!(req_state & REQ_STAY_OPEN))
4690   {
4691     CloseDoor(DOOR_CLOSE_1);
4692
4693     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4694         (req_state & REQ_REOPEN))
4695       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4696   }
4697
4698   RemapAllGadgets();
4699
4700   if (game_status == GAME_MODE_PLAYING)
4701   {
4702     SetPanelBackground();
4703     SetDrawBackgroundMask(REDRAW_DOOR_1);
4704   }
4705   else
4706   {
4707     SetDrawBackgroundMask(REDRAW_FIELD);
4708   }
4709
4710   // continue network game after request
4711   if (network.enabled &&
4712       game_status == GAME_MODE_PLAYING &&
4713       !game.all_players_gone &&
4714       req_state & REQUEST_WAIT_FOR_INPUT)
4715     SendToServer_ContinuePlaying();
4716
4717   // restore deactivated drawing when quick-loading level tape recording
4718   if (tape.playing && tape.deactivate_display)
4719     TapeDeactivateDisplayOn();
4720
4721   return result;
4722 }
4723
4724 static boolean RequestEnvelope(char *text, unsigned int req_state)
4725 {
4726   int result;
4727
4728   if (game_status == GAME_MODE_PLAYING)
4729     BlitScreenToBitmap(backbuffer);
4730
4731   // disable deactivated drawing when quick-loading level tape recording
4732   if (tape.playing && tape.deactivate_display)
4733     TapeDeactivateDisplayOff(TRUE);
4734
4735   SetMouseCursor(CURSOR_DEFAULT);
4736
4737   // pause network game while waiting for request to answer
4738   if (network.enabled &&
4739       game_status == GAME_MODE_PLAYING &&
4740       !game.all_players_gone &&
4741       req_state & REQUEST_WAIT_FOR_INPUT)
4742     SendToServer_PausePlaying();
4743
4744   // simulate releasing mouse button over last gadget, if still pressed
4745   if (button_status)
4746     HandleGadgets(-1, -1, 0);
4747
4748   UnmapAllGadgets();
4749
4750   // (replace with setting corresponding request background)
4751   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4752   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4753
4754   // clear door drawing field
4755   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4756
4757   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4758
4759   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4760   {
4761     if (game_status == GAME_MODE_PLAYING)
4762     {
4763       SetPanelBackground();
4764       SetDrawBackgroundMask(REDRAW_DOOR_1);
4765     }
4766     else
4767     {
4768       SetDrawBackgroundMask(REDRAW_FIELD);
4769     }
4770
4771     return FALSE;
4772   }
4773
4774   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4775
4776   // ---------- handle request buttons ----------
4777   result = RequestHandleEvents(req_state);
4778
4779   UnmapToolButtons();
4780
4781   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4782
4783   RemapAllGadgets();
4784
4785   if (game_status == GAME_MODE_PLAYING)
4786   {
4787     SetPanelBackground();
4788     SetDrawBackgroundMask(REDRAW_DOOR_1);
4789   }
4790   else
4791   {
4792     SetDrawBackgroundMask(REDRAW_FIELD);
4793   }
4794
4795   // continue network game after request
4796   if (network.enabled &&
4797       game_status == GAME_MODE_PLAYING &&
4798       !game.all_players_gone &&
4799       req_state & REQUEST_WAIT_FOR_INPUT)
4800     SendToServer_ContinuePlaying();
4801
4802   // restore deactivated drawing when quick-loading level tape recording
4803   if (tape.playing && tape.deactivate_display)
4804     TapeDeactivateDisplayOn();
4805
4806   return result;
4807 }
4808
4809 boolean Request(char *text, unsigned int req_state)
4810 {
4811   boolean overlay_enabled = GetOverlayEnabled();
4812   boolean result;
4813
4814   game.request_active_or_moving = TRUE;
4815
4816   SetOverlayEnabled(FALSE);
4817
4818   if (global.use_envelope_request)
4819     result = RequestEnvelope(text, req_state);
4820   else
4821     result = RequestDoor(text, req_state);
4822
4823   SetOverlayEnabled(overlay_enabled);
4824
4825   game.request_active_or_moving = FALSE;
4826
4827   return result;
4828 }
4829
4830 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4831 {
4832   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4833   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4834   int compare_result;
4835
4836   if (dpo1->sort_priority != dpo2->sort_priority)
4837     compare_result = dpo1->sort_priority - dpo2->sort_priority;
4838   else
4839     compare_result = dpo1->nr - dpo2->nr;
4840
4841   return compare_result;
4842 }
4843
4844 void InitGraphicCompatibilityInfo_Doors(void)
4845 {
4846   struct
4847   {
4848     int door_token;
4849     int part_1, part_8;
4850     struct DoorInfo *door;
4851   }
4852   doors[] =
4853   {
4854     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
4855     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
4856
4857     { -1,       -1,                     -1,                     NULL    }
4858   };
4859   struct Rect door_rect_list[] =
4860   {
4861     { DX, DY, DXSIZE, DYSIZE },
4862     { VX, VY, VXSIZE, VYSIZE }
4863   };
4864   int i, j;
4865
4866   for (i = 0; doors[i].door_token != -1; i++)
4867   {
4868     int door_token = doors[i].door_token;
4869     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4870     int part_1 = doors[i].part_1;
4871     int part_8 = doors[i].part_8;
4872     int part_2 = part_1 + 1;
4873     int part_3 = part_1 + 2;
4874     struct DoorInfo *door = doors[i].door;
4875     struct Rect *door_rect = &door_rect_list[door_index];
4876     boolean door_gfx_redefined = FALSE;
4877
4878     // check if any door part graphic definitions have been redefined
4879
4880     for (j = 0; door_part_controls[j].door_token != -1; j++)
4881     {
4882       struct DoorPartControlInfo *dpc = &door_part_controls[j];
4883       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4884
4885       if (dpc->door_token == door_token && fi->redefined)
4886         door_gfx_redefined = TRUE;
4887     }
4888
4889     // check for old-style door graphic/animation modifications
4890
4891     if (!door_gfx_redefined)
4892     {
4893       if (door->anim_mode & ANIM_STATIC_PANEL)
4894       {
4895         door->panel.step_xoffset = 0;
4896         door->panel.step_yoffset = 0;
4897       }
4898
4899       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4900       {
4901         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4902         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4903         int num_door_steps, num_panel_steps;
4904
4905         // remove door part graphics other than the two default wings
4906
4907         for (j = 0; door_part_controls[j].door_token != -1; j++)
4908         {
4909           struct DoorPartControlInfo *dpc = &door_part_controls[j];
4910           struct GraphicInfo *g = &graphic_info[dpc->graphic];
4911
4912           if (dpc->graphic >= part_3 &&
4913               dpc->graphic <= part_8)
4914             g->bitmap = NULL;
4915         }
4916
4917         // set graphics and screen positions of the default wings
4918
4919         g_part_1->width  = door_rect->width;
4920         g_part_1->height = door_rect->height;
4921         g_part_2->width  = door_rect->width;
4922         g_part_2->height = door_rect->height;
4923         g_part_2->src_x = door_rect->width;
4924         g_part_2->src_y = g_part_1->src_y;
4925
4926         door->part_2.x = door->part_1.x;
4927         door->part_2.y = door->part_1.y;
4928
4929         if (door->width != -1)
4930         {
4931           g_part_1->width = door->width;
4932           g_part_2->width = door->width;
4933
4934           // special treatment for graphics and screen position of right wing
4935           g_part_2->src_x += door_rect->width - door->width;
4936           door->part_2.x  += door_rect->width - door->width;
4937         }
4938
4939         if (door->height != -1)
4940         {
4941           g_part_1->height = door->height;
4942           g_part_2->height = door->height;
4943
4944           // special treatment for graphics and screen position of bottom wing
4945           g_part_2->src_y += door_rect->height - door->height;
4946           door->part_2.y  += door_rect->height - door->height;
4947         }
4948
4949         // set animation delays for the default wings and panels
4950
4951         door->part_1.step_delay = door->step_delay;
4952         door->part_2.step_delay = door->step_delay;
4953         door->panel.step_delay  = door->step_delay;
4954
4955         // set animation draw order for the default wings
4956
4957         door->part_1.sort_priority = 2; // draw left wing over ...
4958         door->part_2.sort_priority = 1; //          ... right wing
4959
4960         // set animation draw offset for the default wings
4961
4962         if (door->anim_mode & ANIM_HORIZONTAL)
4963         {
4964           door->part_1.step_xoffset = door->step_offset;
4965           door->part_1.step_yoffset = 0;
4966           door->part_2.step_xoffset = door->step_offset * -1;
4967           door->part_2.step_yoffset = 0;
4968
4969           num_door_steps = g_part_1->width / door->step_offset;
4970         }
4971         else    // ANIM_VERTICAL
4972         {
4973           door->part_1.step_xoffset = 0;
4974           door->part_1.step_yoffset = door->step_offset;
4975           door->part_2.step_xoffset = 0;
4976           door->part_2.step_yoffset = door->step_offset * -1;
4977
4978           num_door_steps = g_part_1->height / door->step_offset;
4979         }
4980
4981         // set animation draw offset for the default panels
4982
4983         if (door->step_offset > 1)
4984         {
4985           num_panel_steps = 2 * door_rect->height / door->step_offset;
4986           door->panel.start_step = num_panel_steps - num_door_steps;
4987           door->panel.start_step_closing = door->panel.start_step;
4988         }
4989         else
4990         {
4991           num_panel_steps = door_rect->height / door->step_offset;
4992           door->panel.start_step = num_panel_steps - num_door_steps / 2;
4993           door->panel.start_step_closing = door->panel.start_step;
4994           door->panel.step_delay *= 2;
4995         }
4996       }
4997     }
4998   }
4999 }
5000
5001 void InitDoors(void)
5002 {
5003   int i;
5004
5005   for (i = 0; door_part_controls[i].door_token != -1; i++)
5006   {
5007     struct DoorPartControlInfo *dpc = &door_part_controls[i];
5008     struct DoorPartOrderInfo *dpo = &door_part_order[i];
5009
5010     // initialize "start_step_opening" and "start_step_closing", if needed
5011     if (dpc->pos->start_step_opening == 0 &&
5012         dpc->pos->start_step_closing == 0)
5013     {
5014       // dpc->pos->start_step_opening = dpc->pos->start_step;
5015       dpc->pos->start_step_closing = dpc->pos->start_step;
5016     }
5017
5018     // fill structure for door part draw order (sorted below)
5019     dpo->nr = i;
5020     dpo->sort_priority = dpc->pos->sort_priority;
5021   }
5022
5023   // sort door part controls according to sort_priority and graphic number
5024   qsort(door_part_order, MAX_DOOR_PARTS,
5025         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5026 }
5027
5028 unsigned int OpenDoor(unsigned int door_state)
5029 {
5030   if (door_state & DOOR_COPY_BACK)
5031   {
5032     if (door_state & DOOR_OPEN_1)
5033       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5034                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5035
5036     if (door_state & DOOR_OPEN_2)
5037       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5038                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5039
5040     door_state &= ~DOOR_COPY_BACK;
5041   }
5042
5043   return MoveDoor(door_state);
5044 }
5045
5046 unsigned int CloseDoor(unsigned int door_state)
5047 {
5048   unsigned int old_door_state = GetDoorState();
5049
5050   if (!(door_state & DOOR_NO_COPY_BACK))
5051   {
5052     if (old_door_state & DOOR_OPEN_1)
5053       BlitBitmap(backbuffer, bitmap_db_door_1,
5054                  DX, DY, DXSIZE, DYSIZE, 0, 0);
5055
5056     if (old_door_state & DOOR_OPEN_2)
5057       BlitBitmap(backbuffer, bitmap_db_door_2,
5058                  VX, VY, VXSIZE, VYSIZE, 0, 0);
5059
5060     door_state &= ~DOOR_NO_COPY_BACK;
5061   }
5062
5063   return MoveDoor(door_state);
5064 }
5065
5066 unsigned int GetDoorState(void)
5067 {
5068   return MoveDoor(DOOR_GET_STATE);
5069 }
5070
5071 unsigned int SetDoorState(unsigned int door_state)
5072 {
5073   return MoveDoor(door_state | DOOR_SET_STATE);
5074 }
5075
5076 static int euclid(int a, int b)
5077 {
5078   return (b ? euclid(b, a % b) : a);
5079 }
5080
5081 unsigned int MoveDoor(unsigned int door_state)
5082 {
5083   struct Rect door_rect_list[] =
5084   {
5085     { DX, DY, DXSIZE, DYSIZE },
5086     { VX, VY, VXSIZE, VYSIZE }
5087   };
5088   static int door1 = DOOR_CLOSE_1;
5089   static int door2 = DOOR_CLOSE_2;
5090   unsigned int door_delay = 0;
5091   unsigned int door_delay_value;
5092   int i;
5093
5094   if (door_state == DOOR_GET_STATE)
5095     return (door1 | door2);
5096
5097   if (door_state & DOOR_SET_STATE)
5098   {
5099     if (door_state & DOOR_ACTION_1)
5100       door1 = door_state & DOOR_ACTION_1;
5101     if (door_state & DOOR_ACTION_2)
5102       door2 = door_state & DOOR_ACTION_2;
5103
5104     return (door1 | door2);
5105   }
5106
5107   if (!(door_state & DOOR_FORCE_REDRAW))
5108   {
5109     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5110       door_state &= ~DOOR_OPEN_1;
5111     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5112       door_state &= ~DOOR_CLOSE_1;
5113     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5114       door_state &= ~DOOR_OPEN_2;
5115     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5116       door_state &= ~DOOR_CLOSE_2;
5117   }
5118
5119   if (global.autoplay_leveldir)
5120   {
5121     door_state |= DOOR_NO_DELAY;
5122     door_state &= ~DOOR_CLOSE_ALL;
5123   }
5124
5125   if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5126     door_state |= DOOR_NO_DELAY;
5127
5128   if (door_state & DOOR_ACTION)
5129   {
5130     boolean door_panel_drawn[NUM_DOORS];
5131     boolean panel_has_doors[NUM_DOORS];
5132     boolean door_part_skip[MAX_DOOR_PARTS];
5133     boolean door_part_done[MAX_DOOR_PARTS];
5134     boolean door_part_done_all;
5135     int num_steps[MAX_DOOR_PARTS];
5136     int max_move_delay = 0;     // delay for complete animations of all doors
5137     int max_step_delay = 0;     // delay (ms) between two animation frames
5138     int num_move_steps = 0;     // number of animation steps for all doors
5139     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
5140     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
5141     int current_move_delay = 0;
5142     int start = 0;
5143     int k;
5144
5145     for (i = 0; i < NUM_DOORS; i++)
5146       panel_has_doors[i] = FALSE;
5147
5148     for (i = 0; i < MAX_DOOR_PARTS; i++)
5149     {
5150       struct DoorPartControlInfo *dpc = &door_part_controls[i];
5151       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5152       int door_token = dpc->door_token;
5153
5154       door_part_done[i] = FALSE;
5155       door_part_skip[i] = (!(door_state & door_token) ||
5156                            !g->bitmap);
5157     }
5158
5159     for (i = 0; i < MAX_DOOR_PARTS; i++)
5160     {
5161       int nr = door_part_order[i].nr;
5162       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5163       struct DoorPartPosInfo *pos = dpc->pos;
5164       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5165       int door_token = dpc->door_token;
5166       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5167       boolean is_panel = DOOR_PART_IS_PANEL(nr);
5168       int step_xoffset = ABS(pos->step_xoffset);
5169       int step_yoffset = ABS(pos->step_yoffset);
5170       int step_delay = pos->step_delay;
5171       int current_door_state = door_state & door_token;
5172       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5173       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5174       boolean part_opening = (is_panel ? door_closing : door_opening);
5175       int start_step = (part_opening ? pos->start_step_opening :
5176                         pos->start_step_closing);
5177       float move_xsize = (step_xoffset ? g->width  : 0);
5178       float move_ysize = (step_yoffset ? g->height : 0);
5179       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5180       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5181       int move_steps = (move_xsteps && move_ysteps ?
5182                         MIN(move_xsteps, move_ysteps) :
5183                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
5184       int move_delay = move_steps * step_delay;
5185
5186       if (door_part_skip[nr])
5187         continue;
5188
5189       max_move_delay = MAX(max_move_delay, move_delay);
5190       max_step_delay = (max_step_delay == 0 ? step_delay :
5191                         euclid(max_step_delay, step_delay));
5192       num_steps[nr] = move_steps;
5193
5194       if (!is_panel)
5195       {
5196         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5197
5198         panel_has_doors[door_index] = TRUE;
5199       }
5200     }
5201
5202     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
5203
5204     num_move_steps = max_move_delay / max_step_delay;
5205     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5206
5207     door_delay_value = max_step_delay;
5208
5209     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5210     {
5211       start = num_move_steps - 1;
5212     }
5213     else
5214     {
5215       // opening door sound has priority over simultaneously closing door
5216       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5217       {
5218         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5219
5220         if (door_state & DOOR_OPEN_1)
5221           PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5222         if (door_state & DOOR_OPEN_2)
5223           PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5224       }
5225       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5226       {
5227         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5228
5229         if (door_state & DOOR_CLOSE_1)
5230           PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5231         if (door_state & DOOR_CLOSE_2)
5232           PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5233       }
5234     }
5235
5236     for (k = start; k < num_move_steps; k++)
5237     {
5238       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
5239
5240       door_part_done_all = TRUE;
5241
5242       for (i = 0; i < NUM_DOORS; i++)
5243         door_panel_drawn[i] = FALSE;
5244
5245       for (i = 0; i < MAX_DOOR_PARTS; i++)
5246       {
5247         int nr = door_part_order[i].nr;
5248         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5249         struct DoorPartPosInfo *pos = dpc->pos;
5250         struct GraphicInfo *g = &graphic_info[dpc->graphic];
5251         int door_token = dpc->door_token;
5252         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5253         boolean is_panel = DOOR_PART_IS_PANEL(nr);
5254         boolean is_panel_and_door_has_closed = FALSE;
5255         struct Rect *door_rect = &door_rect_list[door_index];
5256         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5257                                   bitmap_db_door_2);
5258         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5259         int current_door_state = door_state & door_token;
5260         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5261         boolean door_closing = !door_opening;
5262         boolean part_opening = (is_panel ? door_closing : door_opening);
5263         boolean part_closing = !part_opening;
5264         int start_step = (part_opening ? pos->start_step_opening :
5265                           pos->start_step_closing);
5266         int step_delay = pos->step_delay;
5267         int step_factor = step_delay / max_step_delay;
5268         int k1 = (step_factor ? k / step_factor + 1 : k);
5269         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5270         int kk = MAX(0, k2);
5271         int g_src_x = 0;
5272         int g_src_y = 0;
5273         int src_x, src_y, src_xx, src_yy;
5274         int dst_x, dst_y, dst_xx, dst_yy;
5275         int width, height;
5276
5277         if (door_part_skip[nr])
5278           continue;
5279
5280         if (!(door_state & door_token))
5281           continue;
5282
5283         if (!g->bitmap)
5284           continue;
5285
5286         if (!is_panel)
5287         {
5288           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5289           int kk_door = MAX(0, k2_door);
5290           int sync_frame = kk_door * door_delay_value;
5291           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5292
5293           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5294                                 &g_src_x, &g_src_y);
5295         }
5296
5297         // draw door panel
5298
5299         if (!door_panel_drawn[door_index])
5300         {
5301           ClearRectangle(drawto, door_rect->x, door_rect->y,
5302                          door_rect->width, door_rect->height);
5303
5304           door_panel_drawn[door_index] = TRUE;
5305         }
5306
5307         // draw opening or closing door parts
5308
5309         if (pos->step_xoffset < 0)      // door part on right side
5310         {
5311           src_xx = 0;
5312           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5313           width = g->width;
5314
5315           if (dst_xx + width > door_rect->width)
5316             width = door_rect->width - dst_xx;
5317         }
5318         else                            // door part on left side
5319         {
5320           src_xx = 0;
5321           dst_xx = pos->x - kk * pos->step_xoffset;
5322
5323           if (dst_xx < 0)
5324           {
5325             src_xx = ABS(dst_xx);
5326             dst_xx = 0;
5327           }
5328
5329           width = g->width - src_xx;
5330
5331           if (width > door_rect->width)
5332             width = door_rect->width;
5333
5334           // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5335         }
5336
5337         if (pos->step_yoffset < 0)      // door part on bottom side
5338         {
5339           src_yy = 0;
5340           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5341           height = g->height;
5342
5343           if (dst_yy + height > door_rect->height)
5344             height = door_rect->height - dst_yy;
5345         }
5346         else                            // door part on top side
5347         {
5348           src_yy = 0;
5349           dst_yy = pos->y - kk * pos->step_yoffset;
5350
5351           if (dst_yy < 0)
5352           {
5353             src_yy = ABS(dst_yy);
5354             dst_yy = 0;
5355           }
5356
5357           height = g->height - src_yy;
5358         }
5359
5360         src_x = g_src_x + src_xx;
5361         src_y = g_src_y + src_yy;
5362
5363         dst_x = door_rect->x + dst_xx;
5364         dst_y = door_rect->y + dst_yy;
5365
5366         is_panel_and_door_has_closed =
5367           (is_panel &&
5368            door_closing &&
5369            panel_has_doors[door_index] &&
5370            k >= num_move_steps_doors_only - 1);
5371
5372         if (width  >= 0 && width  <= g->width &&
5373             height >= 0 && height <= g->height &&
5374             !is_panel_and_door_has_closed)
5375         {
5376           if (is_panel || !pos->draw_masked)
5377             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5378                        dst_x, dst_y);
5379           else
5380             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5381                              dst_x, dst_y);
5382         }
5383
5384         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5385
5386         if ((part_opening && (width < 0         || height < 0)) ||
5387             (part_closing && (width >= g->width && height >= g->height)))
5388           door_part_done[nr] = TRUE;
5389
5390         // continue door part animations, but not panel after door has closed
5391         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5392           door_part_done_all = FALSE;
5393       }
5394
5395       if (!(door_state & DOOR_NO_DELAY))
5396       {
5397         BackToFront();
5398
5399         SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5400
5401         current_move_delay += max_step_delay;
5402
5403         // prevent OS (Windows) from complaining about program not responding
5404         CheckQuitEvent();
5405       }
5406
5407       if (door_part_done_all)
5408         break;
5409     }
5410
5411     if (!(door_state & DOOR_NO_DELAY))
5412     {
5413       // wait for specified door action post delay
5414       if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5415         door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5416       else if (door_state & DOOR_ACTION_1)
5417         door_delay_value = door_1.post_delay;
5418       else if (door_state & DOOR_ACTION_2)
5419         door_delay_value = door_2.post_delay;
5420
5421       while (!DelayReached(&door_delay, door_delay_value))
5422         BackToFront();
5423     }
5424   }
5425
5426   if (door_state & DOOR_ACTION_1)
5427     door1 = door_state & DOOR_ACTION_1;
5428   if (door_state & DOOR_ACTION_2)
5429     door2 = door_state & DOOR_ACTION_2;
5430
5431   // draw masked border over door area
5432   DrawMaskedBorder(REDRAW_DOOR_1);
5433   DrawMaskedBorder(REDRAW_DOOR_2);
5434
5435   ClearAutoRepeatKeyEvents();
5436
5437   return (door1 | door2);
5438 }
5439
5440 static boolean useSpecialEditorDoor(void)
5441 {
5442   int graphic = IMG_GLOBAL_BORDER_EDITOR;
5443   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5444
5445   // do not draw special editor door if editor border defined or redefined
5446   if (graphic_info[graphic].bitmap != NULL || redefined)
5447     return FALSE;
5448
5449   // do not draw special editor door if global border defined to be empty
5450   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5451     return FALSE;
5452
5453   // do not draw special editor door if viewport definitions do not match
5454   if (EX != VX ||
5455       EY >= VY ||
5456       EXSIZE != VXSIZE ||
5457       EY + EYSIZE != VY + VYSIZE)
5458     return FALSE;
5459
5460   return TRUE;
5461 }
5462
5463 void DrawSpecialEditorDoor(void)
5464 {
5465   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5466   int top_border_width = gfx1->width;
5467   int top_border_height = gfx1->height;
5468   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5469   int ex = EX - outer_border;
5470   int ey = EY - outer_border;
5471   int vy = VY - outer_border;
5472   int exsize = EXSIZE + 2 * outer_border;
5473
5474   if (!useSpecialEditorDoor())
5475     return;
5476
5477   // draw bigger level editor toolbox window
5478   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5479              top_border_width, top_border_height, ex, ey - top_border_height);
5480   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5481              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5482
5483   redraw_mask |= REDRAW_ALL;
5484 }
5485
5486 void UndrawSpecialEditorDoor(void)
5487 {
5488   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5489   int top_border_width = gfx1->width;
5490   int top_border_height = gfx1->height;
5491   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5492   int ex = EX - outer_border;
5493   int ey = EY - outer_border;
5494   int ey_top = ey - top_border_height;
5495   int exsize = EXSIZE + 2 * outer_border;
5496   int eysize = EYSIZE + 2 * outer_border;
5497
5498   if (!useSpecialEditorDoor())
5499     return;
5500
5501   // draw normal tape recorder window
5502   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5503   {
5504     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5505                ex, ey_top, top_border_width, top_border_height,
5506                ex, ey_top);
5507     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5508                ex, ey, exsize, eysize, ex, ey);
5509   }
5510   else
5511   {
5512     // if screen background is set to "[NONE]", clear editor toolbox window
5513     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5514     ClearRectangle(drawto, ex, ey, exsize, eysize);
5515   }
5516
5517   redraw_mask |= REDRAW_ALL;
5518 }
5519
5520
5521 // ---------- new tool button stuff -------------------------------------------
5522
5523 static struct
5524 {
5525   int graphic;
5526   struct TextPosInfo *pos;
5527   int gadget_id;
5528   boolean is_touch_button;
5529   char *infotext;
5530 } toolbutton_info[NUM_TOOL_BUTTONS] =
5531 {
5532   {
5533     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
5534     TOOL_CTRL_ID_YES, FALSE,            "yes"
5535   },
5536   {
5537     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5538     TOOL_CTRL_ID_NO, FALSE,             "no"
5539   },
5540   {
5541     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5542     TOOL_CTRL_ID_CONFIRM, FALSE,        "confirm"
5543   },
5544   {
5545     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5546     TOOL_CTRL_ID_PLAYER_1, FALSE,       "player 1"
5547   },
5548   {
5549     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5550     TOOL_CTRL_ID_PLAYER_2, FALSE,       "player 2"
5551   },
5552   {
5553     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5554     TOOL_CTRL_ID_PLAYER_3, FALSE,       "player 3"
5555   },
5556   {
5557     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5558     TOOL_CTRL_ID_PLAYER_4, FALSE,       "player 4"
5559   },
5560   {
5561     IMG_GFX_REQUEST_BUTTON_TOUCH_YES,   &request.button.touch_yes,
5562     TOOL_CTRL_ID_TOUCH_YES, TRUE,       "yes"
5563   },
5564   {
5565     IMG_GFX_REQUEST_BUTTON_TOUCH_NO,    &request.button.touch_no,
5566     TOOL_CTRL_ID_TOUCH_NO, TRUE,        "no"
5567   },
5568   {
5569     IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5570     TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE,   "confirm"
5571   }
5572 };
5573
5574 void CreateToolButtons(void)
5575 {
5576   int i;
5577
5578   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5579   {
5580     int graphic = toolbutton_info[i].graphic;
5581     struct GraphicInfo *gfx = &graphic_info[graphic];
5582     struct TextPosInfo *pos = toolbutton_info[i].pos;
5583     struct GadgetInfo *gi;
5584     Bitmap *deco_bitmap = None;
5585     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5586     unsigned int event_mask = GD_EVENT_RELEASED;
5587     boolean is_touch_button = toolbutton_info[i].is_touch_button;
5588     int base_x = (is_touch_button ? 0 : DX);
5589     int base_y = (is_touch_button ? 0 : DY);
5590     int gd_x = gfx->src_x;
5591     int gd_y = gfx->src_y;
5592     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5593     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5594     int x = pos->x;
5595     int y = pos->y;
5596     int id = i;
5597
5598     if (global.use_envelope_request && !is_touch_button)
5599     {
5600       setRequestPosition(&base_x, &base_y, TRUE);
5601
5602       // check if request buttons are outside of envelope and fix, if needed
5603       if (x < 0 || x + gfx->width  > request.width ||
5604           y < 0 || y + gfx->height > request.height)
5605       {
5606         if (id == TOOL_CTRL_ID_YES)
5607         {
5608           x = 0;
5609           y = request.height - 2 * request.border_size - gfx->height;
5610         }
5611         else if (id == TOOL_CTRL_ID_NO)
5612         {
5613           x = request.width  - 2 * request.border_size - gfx->width;
5614           y = request.height - 2 * request.border_size - gfx->height;
5615         }
5616         else if (id == TOOL_CTRL_ID_CONFIRM)
5617         {
5618           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5619           y = request.height - 2 * request.border_size - gfx->height;
5620         }
5621         else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5622         {
5623           int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5624
5625           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5626           y = request.height - 2 * request.border_size - gfx->height * 2;
5627
5628           x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5629           y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5630         }
5631       }
5632     }
5633
5634     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5635     {
5636       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5637
5638       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5639                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5640       deco_xpos = (gfx->width  - pos->size) / 2;
5641       deco_ypos = (gfx->height - pos->size) / 2;
5642     }
5643
5644     gi = CreateGadget(GDI_CUSTOM_ID, id,
5645                       GDI_IMAGE_ID, graphic,
5646                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5647                       GDI_X, base_x + x,
5648                       GDI_Y, base_y + y,
5649                       GDI_WIDTH, gfx->width,
5650                       GDI_HEIGHT, gfx->height,
5651                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5652                       GDI_STATE, GD_BUTTON_UNPRESSED,
5653                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5654                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5655                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5656                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5657                       GDI_DECORATION_SIZE, pos->size, pos->size,
5658                       GDI_DECORATION_SHIFTING, 1, 1,
5659                       GDI_DIRECT_DRAW, FALSE,
5660                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5661                       GDI_EVENT_MASK, event_mask,
5662                       GDI_CALLBACK_ACTION, HandleToolButtons,
5663                       GDI_END);
5664
5665     if (gi == NULL)
5666       Fail("cannot create gadget");
5667
5668     tool_gadget[id] = gi;
5669   }
5670 }
5671
5672 void FreeToolButtons(void)
5673 {
5674   int i;
5675
5676   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5677     FreeGadget(tool_gadget[i]);
5678 }
5679
5680 static void UnmapToolButtons(void)
5681 {
5682   int i;
5683
5684   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5685     UnmapGadget(tool_gadget[i]);
5686 }
5687
5688 static void HandleToolButtons(struct GadgetInfo *gi)
5689 {
5690   request_gadget_id = gi->custom_id;
5691 }
5692
5693 static struct Mapping_EM_to_RND_object
5694 {
5695   int element_em;
5696   boolean is_rnd_to_em_mapping;         // unique mapping EM <-> RND
5697   boolean is_backside;                  // backside of moving element
5698
5699   int element_rnd;
5700   int action;
5701   int direction;
5702 }
5703 em_object_mapping_list[GAME_TILE_MAX + 1] =
5704 {
5705   {
5706     Zborder,                            FALSE,  FALSE,
5707     EL_EMPTY,                           -1, -1
5708   },
5709   {
5710     Zplayer,                            FALSE,  FALSE,
5711     EL_EMPTY,                           -1, -1
5712   },
5713
5714   {
5715     Zbug,                               FALSE,  FALSE,
5716     EL_EMPTY,                           -1, -1
5717   },
5718   {
5719     Ztank,                              FALSE,  FALSE,
5720     EL_EMPTY,                           -1, -1
5721   },
5722   {
5723     Zeater,                             FALSE,  FALSE,
5724     EL_EMPTY,                           -1, -1
5725   },
5726   {
5727     Zdynamite,                          FALSE,  FALSE,
5728     EL_EMPTY,                           -1, -1
5729   },
5730   {
5731     Zboom,                              FALSE,  FALSE,
5732     EL_EMPTY,                           -1, -1
5733   },
5734
5735   {
5736     Xchain,                             FALSE,  FALSE,
5737     EL_DEFAULT,                         ACTION_EXPLODING, -1
5738   },
5739   {
5740     Xboom_bug,                          FALSE,  FALSE,
5741     EL_BUG,                             ACTION_EXPLODING, -1
5742   },
5743   {
5744     Xboom_tank,                         FALSE,  FALSE,
5745     EL_SPACESHIP,                       ACTION_EXPLODING, -1
5746   },
5747   {
5748     Xboom_android,                      FALSE,  FALSE,
5749     EL_EMC_ANDROID,                     ACTION_OTHER, -1
5750   },
5751   {
5752     Xboom_1,                            FALSE,  FALSE,
5753     EL_DEFAULT,                         ACTION_EXPLODING, -1
5754   },
5755   {
5756     Xboom_2,                            FALSE,  FALSE,
5757     EL_DEFAULT,                         ACTION_EXPLODING, -1
5758   },
5759
5760   {
5761     Xblank,                             TRUE,   FALSE,
5762     EL_EMPTY,                           -1, -1
5763   },
5764
5765   {
5766     Xsplash_e,                          FALSE,  FALSE,
5767     EL_ACID_SPLASH_RIGHT,               -1, -1
5768   },
5769   {
5770     Xsplash_w,                          FALSE,  FALSE,
5771     EL_ACID_SPLASH_LEFT,                -1, -1
5772   },
5773
5774   {
5775     Xplant,                             TRUE,   FALSE,
5776     EL_EMC_PLANT,                       -1, -1
5777   },
5778   {
5779     Yplant,                             FALSE,  FALSE,
5780     EL_EMC_PLANT,                       -1, -1
5781   },
5782
5783   {
5784     Xacid_1,                            TRUE,   FALSE,
5785     EL_ACID,                            -1, -1
5786   },
5787   {
5788     Xacid_2,                            FALSE,  FALSE,
5789     EL_ACID,                            -1, -1
5790   },
5791   {
5792     Xacid_3,                            FALSE,  FALSE,
5793     EL_ACID,                            -1, -1
5794   },
5795   {
5796     Xacid_4,                            FALSE,  FALSE,
5797     EL_ACID,                            -1, -1
5798   },
5799   {
5800     Xacid_5,                            FALSE,  FALSE,
5801     EL_ACID,                            -1, -1
5802   },
5803   {
5804     Xacid_6,                            FALSE,  FALSE,
5805     EL_ACID,                            -1, -1
5806   },
5807   {
5808     Xacid_7,                            FALSE,  FALSE,
5809     EL_ACID,                            -1, -1
5810   },
5811   {
5812     Xacid_8,                            FALSE,  FALSE,
5813     EL_ACID,                            -1, -1
5814   },
5815
5816   {
5817     Xfake_acid_1,                       TRUE,   FALSE,
5818     EL_EMC_FAKE_ACID,                   -1, -1
5819   },
5820   {
5821     Xfake_acid_2,                       FALSE,  FALSE,
5822     EL_EMC_FAKE_ACID,                   -1, -1
5823   },
5824   {
5825     Xfake_acid_3,                       FALSE,  FALSE,
5826     EL_EMC_FAKE_ACID,                   -1, -1
5827   },
5828   {
5829     Xfake_acid_4,                       FALSE,  FALSE,
5830     EL_EMC_FAKE_ACID,                   -1, -1
5831   },
5832   {
5833     Xfake_acid_5,                       FALSE,  FALSE,
5834     EL_EMC_FAKE_ACID,                   -1, -1
5835   },
5836   {
5837     Xfake_acid_6,                       FALSE,  FALSE,
5838     EL_EMC_FAKE_ACID,                   -1, -1
5839   },
5840   {
5841     Xfake_acid_7,                       FALSE,  FALSE,
5842     EL_EMC_FAKE_ACID,                   -1, -1
5843   },
5844   {
5845     Xfake_acid_8,                       FALSE,  FALSE,
5846     EL_EMC_FAKE_ACID,                   -1, -1
5847   },
5848
5849   {
5850     Xfake_acid_1_player,                FALSE,  FALSE,
5851     EL_EMC_FAKE_ACID,                   -1, -1
5852   },
5853   {
5854     Xfake_acid_2_player,                FALSE,  FALSE,
5855     EL_EMC_FAKE_ACID,                   -1, -1
5856   },
5857   {
5858     Xfake_acid_3_player,                FALSE,  FALSE,
5859     EL_EMC_FAKE_ACID,                   -1, -1
5860   },
5861   {
5862     Xfake_acid_4_player,                FALSE,  FALSE,
5863     EL_EMC_FAKE_ACID,                   -1, -1
5864   },
5865   {
5866     Xfake_acid_5_player,                FALSE,  FALSE,
5867     EL_EMC_FAKE_ACID,                   -1, -1
5868   },
5869   {
5870     Xfake_acid_6_player,                FALSE,  FALSE,
5871     EL_EMC_FAKE_ACID,                   -1, -1
5872   },
5873   {
5874     Xfake_acid_7_player,                FALSE,  FALSE,
5875     EL_EMC_FAKE_ACID,                   -1, -1
5876   },
5877   {
5878     Xfake_acid_8_player,                FALSE,  FALSE,
5879     EL_EMC_FAKE_ACID,                   -1, -1
5880   },
5881
5882   {
5883     Xgrass,                             TRUE,   FALSE,
5884     EL_EMC_GRASS,                       -1, -1
5885   },
5886   {
5887     Ygrass_nB,                          FALSE,  FALSE,
5888     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
5889   },
5890   {
5891     Ygrass_eB,                          FALSE,  FALSE,
5892     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
5893   },
5894   {
5895     Ygrass_sB,                          FALSE,  FALSE,
5896     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
5897   },
5898   {
5899     Ygrass_wB,                          FALSE,  FALSE,
5900     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
5901   },
5902
5903   {
5904     Xdirt,                              TRUE,   FALSE,
5905     EL_SAND,                            -1, -1
5906   },
5907   {
5908     Ydirt_nB,                           FALSE,  FALSE,
5909     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
5910   },
5911   {
5912     Ydirt_eB,                           FALSE,  FALSE,
5913     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
5914   },
5915   {
5916     Ydirt_sB,                           FALSE,  FALSE,
5917     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
5918   },
5919   {
5920     Ydirt_wB,                           FALSE,  FALSE,
5921     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
5922   },
5923
5924   {
5925     Xandroid,                           TRUE,   FALSE,
5926     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
5927   },
5928   {
5929     Xandroid_1_n,                       FALSE,  FALSE,
5930     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5931   },
5932   {
5933     Xandroid_2_n,                       FALSE,  FALSE,
5934     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5935   },
5936   {
5937     Xandroid_1_e,                       FALSE,  FALSE,
5938     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5939   },
5940   {
5941     Xandroid_2_e,                       FALSE,  FALSE,
5942     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5943   },
5944   {
5945     Xandroid_1_w,                       FALSE,  FALSE,
5946     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5947   },
5948   {
5949     Xandroid_2_w,                       FALSE,  FALSE,
5950     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5951   },
5952   {
5953     Xandroid_1_s,                       FALSE,  FALSE,
5954     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5955   },
5956   {
5957     Xandroid_2_s,                       FALSE,  FALSE,
5958     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5959   },
5960   {
5961     Yandroid_n,                         FALSE,  FALSE,
5962     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5963   },
5964   {
5965     Yandroid_nB,                        FALSE,  TRUE,
5966     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5967   },
5968   {
5969     Yandroid_ne,                        FALSE,  FALSE,
5970     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
5971   },
5972   {
5973     Yandroid_neB,                       FALSE,  TRUE,
5974     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
5975   },
5976   {
5977     Yandroid_e,                         FALSE,  FALSE,
5978     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5979   },
5980   {
5981     Yandroid_eB,                        FALSE,  TRUE,
5982     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5983   },
5984   {
5985     Yandroid_se,                        FALSE,  FALSE,
5986     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
5987   },
5988   {
5989     Yandroid_seB,                       FALSE,  TRUE,
5990     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5991   },
5992   {
5993     Yandroid_s,                         FALSE,  FALSE,
5994     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5995   },
5996   {
5997     Yandroid_sB,                        FALSE,  TRUE,
5998     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5999   },
6000   {
6001     Yandroid_sw,                        FALSE,  FALSE,
6002     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
6003   },
6004   {
6005     Yandroid_swB,                       FALSE,  TRUE,
6006     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
6007   },
6008   {
6009     Yandroid_w,                         FALSE,  FALSE,
6010     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6011   },
6012   {
6013     Yandroid_wB,                        FALSE,  TRUE,
6014     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6015   },
6016   {
6017     Yandroid_nw,                        FALSE,  FALSE,
6018     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
6019   },
6020   {
6021     Yandroid_nwB,                       FALSE,  TRUE,
6022     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
6023   },
6024
6025   {
6026     Xeater_n,                           TRUE,   FALSE,
6027     EL_YAMYAM_UP,                       -1, -1
6028   },
6029   {
6030     Xeater_e,                           TRUE,   FALSE,
6031     EL_YAMYAM_RIGHT,                    -1, -1
6032   },
6033   {
6034     Xeater_w,                           TRUE,   FALSE,
6035     EL_YAMYAM_LEFT,                     -1, -1
6036   },
6037   {
6038     Xeater_s,                           TRUE,   FALSE,
6039     EL_YAMYAM_DOWN,                     -1, -1
6040   },
6041   {
6042     Yeater_n,                           FALSE,  FALSE,
6043     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6044   },
6045   {
6046     Yeater_nB,                          FALSE,  TRUE,
6047     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6048   },
6049   {
6050     Yeater_e,                           FALSE,  FALSE,
6051     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6052   },
6053   {
6054     Yeater_eB,                          FALSE,  TRUE,
6055     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6056   },
6057   {
6058     Yeater_s,                           FALSE,  FALSE,
6059     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6060   },
6061   {
6062     Yeater_sB,                          FALSE,  TRUE,
6063     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6064   },
6065   {
6066     Yeater_w,                           FALSE,  FALSE,
6067     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6068   },
6069   {
6070     Yeater_wB,                          FALSE,  TRUE,
6071     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6072   },
6073   {
6074     Yeater_stone,                       FALSE,  FALSE,
6075     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
6076   },
6077   {
6078     Yeater_spring,                      FALSE,  FALSE,
6079     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
6080   },
6081
6082   {
6083     Xalien,                             TRUE,   FALSE,
6084     EL_ROBOT,                           -1, -1
6085   },
6086   {
6087     Xalien_pause,                       FALSE,  FALSE,
6088     EL_ROBOT,                           -1, -1
6089   },
6090   {
6091     Yalien_n,                           FALSE,  FALSE,
6092     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6093   },
6094   {
6095     Yalien_nB,                          FALSE,  TRUE,
6096     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6097   },
6098   {
6099     Yalien_e,                           FALSE,  FALSE,
6100     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6101   },
6102   {
6103     Yalien_eB,                          FALSE,  TRUE,
6104     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6105   },
6106   {
6107     Yalien_s,                           FALSE,  FALSE,
6108     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6109   },
6110   {
6111     Yalien_sB,                          FALSE,  TRUE,
6112     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6113   },
6114   {
6115     Yalien_w,                           FALSE,  FALSE,
6116     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6117   },
6118   {
6119     Yalien_wB,                          FALSE,  TRUE,
6120     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6121   },
6122   {
6123     Yalien_stone,                       FALSE,  FALSE,
6124     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
6125   },
6126   {
6127     Yalien_spring,                      FALSE,  FALSE,
6128     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
6129   },
6130
6131   {
6132     Xbug_1_n,                           TRUE,   FALSE,
6133     EL_BUG_UP,                          -1, -1
6134   },
6135   {
6136     Xbug_1_e,                           TRUE,   FALSE,
6137     EL_BUG_RIGHT,                       -1, -1
6138   },
6139   {
6140     Xbug_1_s,                           TRUE,   FALSE,
6141     EL_BUG_DOWN,                        -1, -1
6142   },
6143   {
6144     Xbug_1_w,                           TRUE,   FALSE,
6145     EL_BUG_LEFT,                        -1, -1
6146   },
6147   {
6148     Xbug_2_n,                           FALSE,  FALSE,
6149     EL_BUG_UP,                          -1, -1
6150   },
6151   {
6152     Xbug_2_e,                           FALSE,  FALSE,
6153     EL_BUG_RIGHT,                       -1, -1
6154   },
6155   {
6156     Xbug_2_s,                           FALSE,  FALSE,
6157     EL_BUG_DOWN,                        -1, -1
6158   },
6159   {
6160     Xbug_2_w,                           FALSE,  FALSE,
6161     EL_BUG_LEFT,                        -1, -1
6162   },
6163   {
6164     Ybug_n,                             FALSE,  FALSE,
6165     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6166   },
6167   {
6168     Ybug_nB,                            FALSE,  TRUE,
6169     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6170   },
6171   {
6172     Ybug_e,                             FALSE,  FALSE,
6173     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6174   },
6175   {
6176     Ybug_eB,                            FALSE,  TRUE,
6177     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6178   },
6179   {
6180     Ybug_s,                             FALSE,  FALSE,
6181     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6182   },
6183   {
6184     Ybug_sB,                            FALSE,  TRUE,
6185     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6186   },
6187   {
6188     Ybug_w,                             FALSE,  FALSE,
6189     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6190   },
6191   {
6192     Ybug_wB,                            FALSE,  TRUE,
6193     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6194   },
6195   {
6196     Ybug_w_n,                           FALSE,  FALSE,
6197     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6198   },
6199   {
6200     Ybug_n_e,                           FALSE,  FALSE,
6201     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6202   },
6203   {
6204     Ybug_e_s,                           FALSE,  FALSE,
6205     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6206   },
6207   {
6208     Ybug_s_w,                           FALSE,  FALSE,
6209     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6210   },
6211   {
6212     Ybug_e_n,                           FALSE,  FALSE,
6213     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6214   },
6215   {
6216     Ybug_s_e,                           FALSE,  FALSE,
6217     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6218   },
6219   {
6220     Ybug_w_s,                           FALSE,  FALSE,
6221     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6222   },
6223   {
6224     Ybug_n_w,                           FALSE,  FALSE,
6225     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6226   },
6227   {
6228     Ybug_stone,                         FALSE,  FALSE,
6229     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
6230   },
6231   {
6232     Ybug_spring,                        FALSE,  FALSE,
6233     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
6234   },
6235
6236   {
6237     Xtank_1_n,                          TRUE,   FALSE,
6238     EL_SPACESHIP_UP,                    -1, -1
6239   },
6240   {
6241     Xtank_1_e,                          TRUE,   FALSE,
6242     EL_SPACESHIP_RIGHT,                 -1, -1
6243   },
6244   {
6245     Xtank_1_s,                          TRUE,   FALSE,
6246     EL_SPACESHIP_DOWN,                  -1, -1
6247   },
6248   {
6249     Xtank_1_w,                          TRUE,   FALSE,
6250     EL_SPACESHIP_LEFT,                  -1, -1
6251   },
6252   {
6253     Xtank_2_n,                          FALSE,  FALSE,
6254     EL_SPACESHIP_UP,                    -1, -1
6255   },
6256   {
6257     Xtank_2_e,                          FALSE,  FALSE,
6258     EL_SPACESHIP_RIGHT,                 -1, -1
6259   },
6260   {
6261     Xtank_2_s,                          FALSE,  FALSE,
6262     EL_SPACESHIP_DOWN,                  -1, -1
6263   },
6264   {
6265     Xtank_2_w,                          FALSE,  FALSE,
6266     EL_SPACESHIP_LEFT,                  -1, -1
6267   },
6268   {
6269     Ytank_n,                            FALSE,  FALSE,
6270     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6271   },
6272   {
6273     Ytank_nB,                           FALSE,  TRUE,
6274     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6275   },
6276   {
6277     Ytank_e,                            FALSE,  FALSE,
6278     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6279   },
6280   {
6281     Ytank_eB,                           FALSE,  TRUE,
6282     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6283   },
6284   {
6285     Ytank_s,                            FALSE,  FALSE,
6286     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6287   },
6288   {
6289     Ytank_sB,                           FALSE,  TRUE,
6290     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6291   },
6292   {
6293     Ytank_w,                            FALSE,  FALSE,
6294     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6295   },
6296   {
6297     Ytank_wB,                           FALSE,  TRUE,
6298     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6299   },
6300   {
6301     Ytank_w_n,                          FALSE,  FALSE,
6302     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6303   },
6304   {
6305     Ytank_n_e,                          FALSE,  FALSE,
6306     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6307   },
6308   {
6309     Ytank_e_s,                          FALSE,  FALSE,
6310     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6311   },
6312   {
6313     Ytank_s_w,                          FALSE,  FALSE,
6314     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6315   },
6316   {
6317     Ytank_e_n,                          FALSE,  FALSE,
6318     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6319   },
6320   {
6321     Ytank_s_e,                          FALSE,  FALSE,
6322     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6323   },
6324   {
6325     Ytank_w_s,                          FALSE,  FALSE,
6326     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6327   },
6328   {
6329     Ytank_n_w,                          FALSE,  FALSE,
6330     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6331   },
6332   {
6333     Ytank_stone,                        FALSE,  FALSE,
6334     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
6335   },
6336   {
6337     Ytank_spring,                       FALSE,  FALSE,
6338     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
6339   },
6340
6341   {
6342     Xemerald,                           TRUE,   FALSE,
6343     EL_EMERALD,                         -1, -1
6344   },
6345   {
6346     Xemerald_pause,                     FALSE,  FALSE,
6347     EL_EMERALD,                         -1, -1
6348   },
6349   {
6350     Xemerald_fall,                      FALSE,  FALSE,
6351     EL_EMERALD,                         -1, -1
6352   },
6353   {
6354     Xemerald_shine,                     FALSE,  FALSE,
6355     EL_EMERALD,                         ACTION_TWINKLING, -1
6356   },
6357   {
6358     Yemerald_s,                         FALSE,  FALSE,
6359     EL_EMERALD,                         ACTION_FALLING, -1
6360   },
6361   {
6362     Yemerald_sB,                        FALSE,  TRUE,
6363     EL_EMERALD,                         ACTION_FALLING, -1
6364   },
6365   {
6366     Yemerald_e,                         FALSE,  FALSE,
6367     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6368   },
6369   {
6370     Yemerald_eB,                        FALSE,  TRUE,
6371     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6372   },
6373   {
6374     Yemerald_w,                         FALSE,  FALSE,
6375     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6376   },
6377   {
6378     Yemerald_wB,                        FALSE,  TRUE,
6379     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6380   },
6381   {
6382     Yemerald_blank,                     FALSE,  FALSE,
6383     EL_EMERALD,                         ACTION_COLLECTING, -1
6384   },
6385
6386   {
6387     Xdiamond,                           TRUE,   FALSE,
6388     EL_DIAMOND,                         -1, -1
6389   },
6390   {
6391     Xdiamond_pause,                     FALSE,  FALSE,
6392     EL_DIAMOND,                         -1, -1
6393   },
6394   {
6395     Xdiamond_fall,                      FALSE,  FALSE,
6396     EL_DIAMOND,                         -1, -1
6397   },
6398   {
6399     Xdiamond_shine,                     FALSE,  FALSE,
6400     EL_DIAMOND,                         ACTION_TWINKLING, -1
6401   },
6402   {
6403     Ydiamond_s,                         FALSE,  FALSE,
6404     EL_DIAMOND,                         ACTION_FALLING, -1
6405   },
6406   {
6407     Ydiamond_sB,                        FALSE,  TRUE,
6408     EL_DIAMOND,                         ACTION_FALLING, -1
6409   },
6410   {
6411     Ydiamond_e,                         FALSE,  FALSE,
6412     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6413   },
6414   {
6415     Ydiamond_eB,                        FALSE,  TRUE,
6416     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6417   },
6418   {
6419     Ydiamond_w,                         FALSE,  FALSE,
6420     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6421   },
6422   {
6423     Ydiamond_wB,                        FALSE,  TRUE,
6424     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6425   },
6426   {
6427     Ydiamond_blank,                     FALSE,  FALSE,
6428     EL_DIAMOND,                         ACTION_COLLECTING, -1
6429   },
6430   {
6431     Ydiamond_stone,                     FALSE,  FALSE,
6432     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
6433   },
6434
6435   {
6436     Xstone,                             TRUE,   FALSE,
6437     EL_ROCK,                            -1, -1
6438   },
6439   {
6440     Xstone_pause,                       FALSE,  FALSE,
6441     EL_ROCK,                            -1, -1
6442   },
6443   {
6444     Xstone_fall,                        FALSE,  FALSE,
6445     EL_ROCK,                            -1, -1
6446   },
6447   {
6448     Ystone_s,                           FALSE,  FALSE,
6449     EL_ROCK,                            ACTION_FALLING, -1
6450   },
6451   {
6452     Ystone_sB,                          FALSE,  TRUE,
6453     EL_ROCK,                            ACTION_FALLING, -1
6454   },
6455   {
6456     Ystone_e,                           FALSE,  FALSE,
6457     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6458   },
6459   {
6460     Ystone_eB,                          FALSE,  TRUE,
6461     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6462   },
6463   {
6464     Ystone_w,                           FALSE,  FALSE,
6465     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6466   },
6467   {
6468     Ystone_wB,                          FALSE,  TRUE,
6469     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6470   },
6471
6472   {
6473     Xbomb,                              TRUE,   FALSE,
6474     EL_BOMB,                            -1, -1
6475   },
6476   {
6477     Xbomb_pause,                        FALSE,  FALSE,
6478     EL_BOMB,                            -1, -1
6479   },
6480   {
6481     Xbomb_fall,                         FALSE,  FALSE,
6482     EL_BOMB,                            -1, -1
6483   },
6484   {
6485     Ybomb_s,                            FALSE,  FALSE,
6486     EL_BOMB,                            ACTION_FALLING, -1
6487   },
6488   {
6489     Ybomb_sB,                           FALSE,  TRUE,
6490     EL_BOMB,                            ACTION_FALLING, -1
6491   },
6492   {
6493     Ybomb_e,                            FALSE,  FALSE,
6494     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6495   },
6496   {
6497     Ybomb_eB,                           FALSE,  TRUE,
6498     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6499   },
6500   {
6501     Ybomb_w,                            FALSE,  FALSE,
6502     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6503   },
6504   {
6505     Ybomb_wB,                           FALSE,  TRUE,
6506     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6507   },
6508   {
6509     Ybomb_blank,                        FALSE,  FALSE,
6510     EL_BOMB,                            ACTION_ACTIVATING, -1
6511   },
6512
6513   {
6514     Xnut,                               TRUE,   FALSE,
6515     EL_NUT,                             -1, -1
6516   },
6517   {
6518     Xnut_pause,                         FALSE,  FALSE,
6519     EL_NUT,                             -1, -1
6520   },
6521   {
6522     Xnut_fall,                          FALSE,  FALSE,
6523     EL_NUT,                             -1, -1
6524   },
6525   {
6526     Ynut_s,                             FALSE,  FALSE,
6527     EL_NUT,                             ACTION_FALLING, -1
6528   },
6529   {
6530     Ynut_sB,                            FALSE,  TRUE,
6531     EL_NUT,                             ACTION_FALLING, -1
6532   },
6533   {
6534     Ynut_e,                             FALSE,  FALSE,
6535     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6536   },
6537   {
6538     Ynut_eB,                            FALSE,  TRUE,
6539     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6540   },
6541   {
6542     Ynut_w,                             FALSE,  FALSE,
6543     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6544   },
6545   {
6546     Ynut_wB,                            FALSE,  TRUE,
6547     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6548   },
6549   {
6550     Ynut_stone,                         FALSE,  FALSE,
6551     EL_NUT,                             ACTION_BREAKING, -1
6552   },
6553
6554   {
6555     Xspring,                            TRUE,   FALSE,
6556     EL_SPRING,                          -1, -1
6557   },
6558   {
6559     Xspring_pause,                      FALSE,  FALSE,
6560     EL_SPRING,                          -1, -1
6561   },
6562   {
6563     Xspring_e,                          TRUE,   FALSE,
6564     EL_SPRING_RIGHT,                    -1, -1
6565   },
6566   {
6567     Xspring_w,                          TRUE,   FALSE,
6568     EL_SPRING_LEFT,                     -1, -1
6569   },
6570   {
6571     Xspring_fall,                       FALSE,  FALSE,
6572     EL_SPRING,                          -1, -1
6573   },
6574   {
6575     Yspring_s,                          FALSE,  FALSE,
6576     EL_SPRING,                          ACTION_FALLING, -1
6577   },
6578   {
6579     Yspring_sB,                         FALSE,  TRUE,
6580     EL_SPRING,                          ACTION_FALLING, -1
6581   },
6582   {
6583     Yspring_e,                          FALSE,  FALSE,
6584     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6585   },
6586   {
6587     Yspring_eB,                         FALSE,  TRUE,
6588     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6589   },
6590   {
6591     Yspring_w,                          FALSE,  FALSE,
6592     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6593   },
6594   {
6595     Yspring_wB,                         FALSE,  TRUE,
6596     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6597   },
6598   {
6599     Yspring_alien_e,                    FALSE,  FALSE,
6600     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6601   },
6602   {
6603     Yspring_alien_eB,                   FALSE,  TRUE,
6604     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6605   },
6606   {
6607     Yspring_alien_w,                    FALSE,  FALSE,
6608     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6609   },
6610   {
6611     Yspring_alien_wB,                   FALSE,  TRUE,
6612     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6613   },
6614
6615   {
6616     Xpush_emerald_e,                    FALSE,  FALSE,
6617     EL_EMERALD,                         -1, MV_BIT_RIGHT
6618   },
6619   {
6620     Xpush_emerald_w,                    FALSE,  FALSE,
6621     EL_EMERALD,                         -1, MV_BIT_LEFT
6622   },
6623   {
6624     Xpush_diamond_e,                    FALSE,  FALSE,
6625     EL_DIAMOND,                         -1, MV_BIT_RIGHT
6626   },
6627   {
6628     Xpush_diamond_w,                    FALSE,  FALSE,
6629     EL_DIAMOND,                         -1, MV_BIT_LEFT
6630   },
6631   {
6632     Xpush_stone_e,                      FALSE,  FALSE,
6633     EL_ROCK,                            -1, MV_BIT_RIGHT
6634   },
6635   {
6636     Xpush_stone_w,                      FALSE,  FALSE,
6637     EL_ROCK,                            -1, MV_BIT_LEFT
6638   },
6639   {
6640     Xpush_bomb_e,                       FALSE,  FALSE,
6641     EL_BOMB,                            -1, MV_BIT_RIGHT
6642   },
6643   {
6644     Xpush_bomb_w,                       FALSE,  FALSE,
6645     EL_BOMB,                            -1, MV_BIT_LEFT
6646   },
6647   {
6648     Xpush_nut_e,                        FALSE,  FALSE,
6649     EL_NUT,                             -1, MV_BIT_RIGHT
6650   },
6651   {
6652     Xpush_nut_w,                        FALSE,  FALSE,
6653     EL_NUT,                             -1, MV_BIT_LEFT
6654   },
6655   {
6656     Xpush_spring_e,                     FALSE,  FALSE,
6657     EL_SPRING_RIGHT,                    -1, MV_BIT_RIGHT
6658   },
6659   {
6660     Xpush_spring_w,                     FALSE,  FALSE,
6661     EL_SPRING_LEFT,                     -1, MV_BIT_LEFT
6662   },
6663
6664   {
6665     Xdynamite,                          TRUE,   FALSE,
6666     EL_EM_DYNAMITE,                     -1, -1
6667   },
6668   {
6669     Ydynamite_blank,                    FALSE,  FALSE,
6670     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6671   },
6672   {
6673     Xdynamite_1,                        TRUE,   FALSE,
6674     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6675   },
6676   {
6677     Xdynamite_2,                        FALSE,  FALSE,
6678     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6679   },
6680   {
6681     Xdynamite_3,                        FALSE,  FALSE,
6682     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6683   },
6684   {
6685     Xdynamite_4,                        FALSE,  FALSE,
6686     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6687   },
6688
6689   {
6690     Xkey_1,                             TRUE,   FALSE,
6691     EL_EM_KEY_1,                        -1, -1
6692   },
6693   {
6694     Xkey_2,                             TRUE,   FALSE,
6695     EL_EM_KEY_2,                        -1, -1
6696   },
6697   {
6698     Xkey_3,                             TRUE,   FALSE,
6699     EL_EM_KEY_3,                        -1, -1
6700   },
6701   {
6702     Xkey_4,                             TRUE,   FALSE,
6703     EL_EM_KEY_4,                        -1, -1
6704   },
6705   {
6706     Xkey_5,                             TRUE,   FALSE,
6707     EL_EMC_KEY_5,                       -1, -1
6708   },
6709   {
6710     Xkey_6,                             TRUE,   FALSE,
6711     EL_EMC_KEY_6,                       -1, -1
6712   },
6713   {
6714     Xkey_7,                             TRUE,   FALSE,
6715     EL_EMC_KEY_7,                       -1, -1
6716   },
6717   {
6718     Xkey_8,                             TRUE,   FALSE,
6719     EL_EMC_KEY_8,                       -1, -1
6720   },
6721
6722   {
6723     Xdoor_1,                            TRUE,   FALSE,
6724     EL_EM_GATE_1,                       -1, -1
6725   },
6726   {
6727     Xdoor_2,                            TRUE,   FALSE,
6728     EL_EM_GATE_2,                       -1, -1
6729   },
6730   {
6731     Xdoor_3,                            TRUE,   FALSE,
6732     EL_EM_GATE_3,                       -1, -1
6733   },
6734   {
6735     Xdoor_4,                            TRUE,   FALSE,
6736     EL_EM_GATE_4,                       -1, -1
6737   },
6738   {
6739     Xdoor_5,                            TRUE,   FALSE,
6740     EL_EMC_GATE_5,                      -1, -1
6741   },
6742   {
6743     Xdoor_6,                            TRUE,   FALSE,
6744     EL_EMC_GATE_6,                      -1, -1
6745   },
6746   {
6747     Xdoor_7,                            TRUE,   FALSE,
6748     EL_EMC_GATE_7,                      -1, -1
6749   },
6750   {
6751     Xdoor_8,                            TRUE,   FALSE,
6752     EL_EMC_GATE_8,                      -1, -1
6753   },
6754
6755   {
6756     Xfake_door_1,                       TRUE,   FALSE,
6757     EL_EM_GATE_1_GRAY,                  -1, -1
6758   },
6759   {
6760     Xfake_door_2,                       TRUE,   FALSE,
6761     EL_EM_GATE_2_GRAY,                  -1, -1
6762   },
6763   {
6764     Xfake_door_3,                       TRUE,   FALSE,
6765     EL_EM_GATE_3_GRAY,                  -1, -1
6766   },
6767   {
6768     Xfake_door_4,                       TRUE,   FALSE,
6769     EL_EM_GATE_4_GRAY,                  -1, -1
6770   },
6771   {
6772     Xfake_door_5,                       TRUE,   FALSE,
6773     EL_EMC_GATE_5_GRAY,                 -1, -1
6774   },
6775   {
6776     Xfake_door_6,                       TRUE,   FALSE,
6777     EL_EMC_GATE_6_GRAY,                 -1, -1
6778   },
6779   {
6780     Xfake_door_7,                       TRUE,   FALSE,
6781     EL_EMC_GATE_7_GRAY,                 -1, -1
6782   },
6783   {
6784     Xfake_door_8,                       TRUE,   FALSE,
6785     EL_EMC_GATE_8_GRAY,                 -1, -1
6786   },
6787
6788   {
6789     Xballoon,                           TRUE,   FALSE,
6790     EL_BALLOON,                         -1, -1
6791   },
6792   {
6793     Yballoon_n,                         FALSE,  FALSE,
6794     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6795   },
6796   {
6797     Yballoon_nB,                        FALSE,  TRUE,
6798     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6799   },
6800   {
6801     Yballoon_e,                         FALSE,  FALSE,
6802     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6803   },
6804   {
6805     Yballoon_eB,                        FALSE,  TRUE,
6806     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6807   },
6808   {
6809     Yballoon_s,                         FALSE,  FALSE,
6810     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6811   },
6812   {
6813     Yballoon_sB,                        FALSE,  TRUE,
6814     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6815   },
6816   {
6817     Yballoon_w,                         FALSE,  FALSE,
6818     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6819   },
6820   {
6821     Yballoon_wB,                        FALSE,  TRUE,
6822     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6823   },
6824
6825   {
6826     Xball_1,                            TRUE,   FALSE,
6827     EL_EMC_MAGIC_BALL,                  -1, -1
6828   },
6829   {
6830     Yball_1,                            FALSE,  FALSE,
6831     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6832   },
6833   {
6834     Xball_2,                            FALSE,  FALSE,
6835     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6836   },
6837   {
6838     Yball_2,                            FALSE,  FALSE,
6839     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6840   },
6841   {
6842     Yball_blank,                        FALSE,  FALSE,
6843     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
6844   },
6845
6846   {
6847     Xamoeba_1,                          TRUE,   FALSE,
6848     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6849   },
6850   {
6851     Xamoeba_2,                          FALSE,  FALSE,
6852     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6853   },
6854   {
6855     Xamoeba_3,                          FALSE,  FALSE,
6856     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6857   },
6858   {
6859     Xamoeba_4,                          FALSE,  FALSE,
6860     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6861   },
6862   {
6863     Xamoeba_5,                          TRUE,   FALSE,
6864     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6865   },
6866   {
6867     Xamoeba_6,                          FALSE,  FALSE,
6868     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6869   },
6870   {
6871     Xamoeba_7,                          FALSE,  FALSE,
6872     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6873   },
6874   {
6875     Xamoeba_8,                          FALSE,  FALSE,
6876     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6877   },
6878
6879   {
6880     Xdrip,                              TRUE,   FALSE,
6881     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
6882   },
6883   {
6884     Xdrip_fall,                         FALSE,  FALSE,
6885     EL_AMOEBA_DROP,                     -1, -1
6886   },
6887   {
6888     Xdrip_stretch,                      FALSE,  FALSE,
6889     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6890   },
6891   {
6892     Xdrip_stretchB,                     FALSE,  TRUE,
6893     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6894   },
6895   {
6896     Ydrip_1_s,                          FALSE,  FALSE,
6897     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6898   },
6899   {
6900     Ydrip_1_sB,                         FALSE,  TRUE,
6901     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6902   },
6903   {
6904     Ydrip_2_s,                          FALSE,  FALSE,
6905     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6906   },
6907   {
6908     Ydrip_2_sB,                         FALSE,  TRUE,
6909     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6910   },
6911
6912   {
6913     Xwonderwall,                        TRUE,   FALSE,
6914     EL_MAGIC_WALL,                      -1, -1
6915   },
6916   {
6917     Ywonderwall,                        FALSE,  FALSE,
6918     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
6919   },
6920
6921   {
6922     Xwheel,                             TRUE,   FALSE,
6923     EL_ROBOT_WHEEL,                     -1, -1
6924   },
6925   {
6926     Ywheel,                             FALSE,  FALSE,
6927     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
6928   },
6929
6930   {
6931     Xswitch,                            TRUE,   FALSE,
6932     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
6933   },
6934   {
6935     Yswitch,                            FALSE,  FALSE,
6936     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
6937   },
6938
6939   {
6940     Xbumper,                            TRUE,   FALSE,
6941     EL_EMC_SPRING_BUMPER,               -1, -1
6942   },
6943   {
6944     Ybumper,                            FALSE,  FALSE,
6945     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
6946   },
6947
6948   {
6949     Xacid_nw,                           TRUE,   FALSE,
6950     EL_ACID_POOL_TOPLEFT,               -1, -1
6951   },
6952   {
6953     Xacid_ne,                           TRUE,   FALSE,
6954     EL_ACID_POOL_TOPRIGHT,              -1, -1
6955   },
6956   {
6957     Xacid_sw,                           TRUE,   FALSE,
6958     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
6959   },
6960   {
6961     Xacid_s,                            TRUE,   FALSE,
6962     EL_ACID_POOL_BOTTOM,                -1, -1
6963   },
6964   {
6965     Xacid_se,                           TRUE,   FALSE,
6966     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
6967   },
6968
6969   {
6970     Xfake_blank,                        TRUE,   FALSE,
6971     EL_INVISIBLE_WALL,                  -1, -1
6972   },
6973   {
6974     Yfake_blank,                        FALSE,  FALSE,
6975     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
6976   },
6977
6978   {
6979     Xfake_grass,                        TRUE,   FALSE,
6980     EL_EMC_FAKE_GRASS,                  -1, -1
6981   },
6982   {
6983     Yfake_grass,                        FALSE,  FALSE,
6984     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
6985   },
6986
6987   {
6988     Xfake_amoeba,                       TRUE,   FALSE,
6989     EL_EMC_DRIPPER,                     -1, -1
6990   },
6991   {
6992     Yfake_amoeba,                       FALSE,  FALSE,
6993     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
6994   },
6995
6996   {
6997     Xlenses,                            TRUE,   FALSE,
6998     EL_EMC_LENSES,                      -1, -1
6999   },
7000
7001   {
7002     Xmagnify,                           TRUE,   FALSE,
7003     EL_EMC_MAGNIFIER,                   -1, -1
7004   },
7005
7006   {
7007     Xsand,                              TRUE,   FALSE,
7008     EL_QUICKSAND_EMPTY,                 -1, -1
7009   },
7010   {
7011     Xsand_stone,                        TRUE,   FALSE,
7012     EL_QUICKSAND_FULL,                  -1, -1
7013   },
7014   {
7015     Xsand_stonein_1,                    FALSE,  TRUE,
7016     EL_ROCK,                            ACTION_FILLING, -1
7017   },
7018   {
7019     Xsand_stonein_2,                    FALSE,  TRUE,
7020     EL_ROCK,                            ACTION_FILLING, -1
7021   },
7022   {
7023     Xsand_stonein_3,                    FALSE,  TRUE,
7024     EL_ROCK,                            ACTION_FILLING, -1
7025   },
7026   {
7027     Xsand_stonein_4,                    FALSE,  TRUE,
7028     EL_ROCK,                            ACTION_FILLING, -1
7029   },
7030   {
7031     Xsand_sandstone_1,                  FALSE,  FALSE,
7032     EL_QUICKSAND_FILLING,               -1, -1
7033   },
7034   {
7035     Xsand_sandstone_2,                  FALSE,  FALSE,
7036     EL_QUICKSAND_FILLING,               -1, -1
7037   },
7038   {
7039     Xsand_sandstone_3,                  FALSE,  FALSE,
7040     EL_QUICKSAND_FILLING,               -1, -1
7041   },
7042   {
7043     Xsand_sandstone_4,                  FALSE,  FALSE,
7044     EL_QUICKSAND_FILLING,               -1, -1
7045   },
7046   {
7047     Xsand_stonesand_1,                  FALSE,  FALSE,
7048     EL_QUICKSAND_EMPTYING,              -1, -1
7049   },
7050   {
7051     Xsand_stonesand_2,                  FALSE,  FALSE,
7052     EL_QUICKSAND_EMPTYING,              -1, -1
7053   },
7054   {
7055     Xsand_stonesand_3,                  FALSE,  FALSE,
7056     EL_QUICKSAND_EMPTYING,              -1, -1
7057   },
7058   {
7059     Xsand_stonesand_4,                  FALSE,  FALSE,
7060     EL_QUICKSAND_EMPTYING,              -1, -1
7061   },
7062   {
7063     Xsand_stoneout_1,                   FALSE,  FALSE,
7064     EL_ROCK,                            ACTION_EMPTYING, -1
7065   },
7066   {
7067     Xsand_stoneout_2,                   FALSE,  FALSE,
7068     EL_ROCK,                            ACTION_EMPTYING, -1
7069   },
7070   {
7071     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
7072     EL_QUICKSAND_EMPTYING,              -1, -1
7073   },
7074   {
7075     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
7076     EL_QUICKSAND_EMPTYING,              -1, -1
7077   },
7078
7079   {
7080     Xslide_ns,                          TRUE,   FALSE,
7081     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
7082   },
7083   {
7084     Yslide_ns_blank,                    FALSE,  FALSE,
7085     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
7086   },
7087   {
7088     Xslide_ew,                          TRUE,   FALSE,
7089     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
7090   },
7091   {
7092     Yslide_ew_blank,                    FALSE,  FALSE,
7093     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
7094   },
7095
7096   {
7097     Xwind_n,                            TRUE,   FALSE,
7098     EL_BALLOON_SWITCH_UP,               -1, -1
7099   },
7100   {
7101     Xwind_e,                            TRUE,   FALSE,
7102     EL_BALLOON_SWITCH_RIGHT,            -1, -1
7103   },
7104   {
7105     Xwind_s,                            TRUE,   FALSE,
7106     EL_BALLOON_SWITCH_DOWN,             -1, -1
7107   },
7108   {
7109     Xwind_w,                            TRUE,   FALSE,
7110     EL_BALLOON_SWITCH_LEFT,             -1, -1
7111   },
7112   {
7113     Xwind_any,                          TRUE,   FALSE,
7114     EL_BALLOON_SWITCH_ANY,              -1, -1
7115   },
7116   {
7117     Xwind_stop,                         TRUE,   FALSE,
7118     EL_BALLOON_SWITCH_NONE,             -1, -1
7119   },
7120
7121   {
7122     Xexit,                              TRUE,   FALSE,
7123     EL_EM_EXIT_CLOSED,                  -1, -1
7124   },
7125   {
7126     Xexit_1,                            TRUE,   FALSE,
7127     EL_EM_EXIT_OPEN,                    -1, -1
7128   },
7129   {
7130     Xexit_2,                            FALSE,  FALSE,
7131     EL_EM_EXIT_OPEN,                    -1, -1
7132   },
7133   {
7134     Xexit_3,                            FALSE,  FALSE,
7135     EL_EM_EXIT_OPEN,                    -1, -1
7136   },
7137
7138   {
7139     Xpause,                             FALSE,  FALSE,
7140     EL_EMPTY,                           -1, -1
7141   },
7142
7143   {
7144     Xwall_1,                            TRUE,   FALSE,
7145     EL_WALL,                            -1, -1
7146   },
7147   {
7148     Xwall_2,                            TRUE,   FALSE,
7149     EL_EMC_WALL_14,                     -1, -1
7150   },
7151   {
7152     Xwall_3,                            TRUE,   FALSE,
7153     EL_EMC_WALL_15,                     -1, -1
7154   },
7155   {
7156     Xwall_4,                            TRUE,   FALSE,
7157     EL_EMC_WALL_16,                     -1, -1
7158   },
7159
7160   {
7161     Xroundwall_1,                       TRUE,   FALSE,
7162     EL_WALL_SLIPPERY,                   -1, -1
7163   },
7164   {
7165     Xroundwall_2,                       TRUE,   FALSE,
7166     EL_EMC_WALL_SLIPPERY_2,             -1, -1
7167   },
7168   {
7169     Xroundwall_3,                       TRUE,   FALSE,
7170     EL_EMC_WALL_SLIPPERY_3,             -1, -1
7171   },
7172   {
7173     Xroundwall_4,                       TRUE,   FALSE,
7174     EL_EMC_WALL_SLIPPERY_4,             -1, -1
7175   },
7176
7177   {
7178     Xsteel_1,                           TRUE,   FALSE,
7179     EL_STEELWALL,                       -1, -1
7180   },
7181   {
7182     Xsteel_2,                           TRUE,   FALSE,
7183     EL_EMC_STEELWALL_2,                 -1, -1
7184   },
7185   {
7186     Xsteel_3,                           TRUE,   FALSE,
7187     EL_EMC_STEELWALL_3,                 -1, -1
7188   },
7189   {
7190     Xsteel_4,                           TRUE,   FALSE,
7191     EL_EMC_STEELWALL_4,                 -1, -1
7192   },
7193
7194   {
7195     Xdecor_1,                           TRUE,   FALSE,
7196     EL_EMC_WALL_8,                      -1, -1
7197   },
7198   {
7199     Xdecor_2,                           TRUE,   FALSE,
7200     EL_EMC_WALL_6,                      -1, -1
7201   },
7202   {
7203     Xdecor_3,                           TRUE,   FALSE,
7204     EL_EMC_WALL_4,                      -1, -1
7205   },
7206   {
7207     Xdecor_4,                           TRUE,   FALSE,
7208     EL_EMC_WALL_7,                      -1, -1
7209   },
7210   {
7211     Xdecor_5,                           TRUE,   FALSE,
7212     EL_EMC_WALL_5,                      -1, -1
7213   },
7214   {
7215     Xdecor_6,                           TRUE,   FALSE,
7216     EL_EMC_WALL_9,                      -1, -1
7217   },
7218   {
7219     Xdecor_7,                           TRUE,   FALSE,
7220     EL_EMC_WALL_10,                     -1, -1
7221   },
7222   {
7223     Xdecor_8,                           TRUE,   FALSE,
7224     EL_EMC_WALL_1,                      -1, -1
7225   },
7226   {
7227     Xdecor_9,                           TRUE,   FALSE,
7228     EL_EMC_WALL_2,                      -1, -1
7229   },
7230   {
7231     Xdecor_10,                          TRUE,   FALSE,
7232     EL_EMC_WALL_3,                      -1, -1
7233   },
7234   {
7235     Xdecor_11,                          TRUE,   FALSE,
7236     EL_EMC_WALL_11,                     -1, -1
7237   },
7238   {
7239     Xdecor_12,                          TRUE,   FALSE,
7240     EL_EMC_WALL_12,                     -1, -1
7241   },
7242
7243   {
7244     Xalpha_0,                           TRUE,   FALSE,
7245     EL_CHAR('0'),                       -1, -1
7246   },
7247   {
7248     Xalpha_1,                           TRUE,   FALSE,
7249     EL_CHAR('1'),                       -1, -1
7250   },
7251   {
7252     Xalpha_2,                           TRUE,   FALSE,
7253     EL_CHAR('2'),                       -1, -1
7254   },
7255   {
7256     Xalpha_3,                           TRUE,   FALSE,
7257     EL_CHAR('3'),                       -1, -1
7258   },
7259   {
7260     Xalpha_4,                           TRUE,   FALSE,
7261     EL_CHAR('4'),                       -1, -1
7262   },
7263   {
7264     Xalpha_5,                           TRUE,   FALSE,
7265     EL_CHAR('5'),                       -1, -1
7266   },
7267   {
7268     Xalpha_6,                           TRUE,   FALSE,
7269     EL_CHAR('6'),                       -1, -1
7270   },
7271   {
7272     Xalpha_7,                           TRUE,   FALSE,
7273     EL_CHAR('7'),                       -1, -1
7274   },
7275   {
7276     Xalpha_8,                           TRUE,   FALSE,
7277     EL_CHAR('8'),                       -1, -1
7278   },
7279   {
7280     Xalpha_9,                           TRUE,   FALSE,
7281     EL_CHAR('9'),                       -1, -1
7282   },
7283   {
7284     Xalpha_excla,                       TRUE,   FALSE,
7285     EL_CHAR('!'),                       -1, -1
7286   },
7287   {
7288     Xalpha_apost,                       TRUE,   FALSE,
7289     EL_CHAR('\''),                      -1, -1
7290   },
7291   {
7292     Xalpha_comma,                       TRUE,   FALSE,
7293     EL_CHAR(','),                       -1, -1
7294   },
7295   {
7296     Xalpha_minus,                       TRUE,   FALSE,
7297     EL_CHAR('-'),                       -1, -1
7298   },
7299   {
7300     Xalpha_perio,                       TRUE,   FALSE,
7301     EL_CHAR('.'),                       -1, -1
7302   },
7303   {
7304     Xalpha_colon,                       TRUE,   FALSE,
7305     EL_CHAR(':'),                       -1, -1
7306   },
7307   {
7308     Xalpha_quest,                       TRUE,   FALSE,
7309     EL_CHAR('?'),                       -1, -1
7310   },
7311   {
7312     Xalpha_a,                           TRUE,   FALSE,
7313     EL_CHAR('A'),                       -1, -1
7314   },
7315   {
7316     Xalpha_b,                           TRUE,   FALSE,
7317     EL_CHAR('B'),                       -1, -1
7318   },
7319   {
7320     Xalpha_c,                           TRUE,   FALSE,
7321     EL_CHAR('C'),                       -1, -1
7322   },
7323   {
7324     Xalpha_d,                           TRUE,   FALSE,
7325     EL_CHAR('D'),                       -1, -1
7326   },
7327   {
7328     Xalpha_e,                           TRUE,   FALSE,
7329     EL_CHAR('E'),                       -1, -1
7330   },
7331   {
7332     Xalpha_f,                           TRUE,   FALSE,
7333     EL_CHAR('F'),                       -1, -1
7334   },
7335   {
7336     Xalpha_g,                           TRUE,   FALSE,
7337     EL_CHAR('G'),                       -1, -1
7338   },
7339   {
7340     Xalpha_h,                           TRUE,   FALSE,
7341     EL_CHAR('H'),                       -1, -1
7342   },
7343   {
7344     Xalpha_i,                           TRUE,   FALSE,
7345     EL_CHAR('I'),                       -1, -1
7346   },
7347   {
7348     Xalpha_j,                           TRUE,   FALSE,
7349     EL_CHAR('J'),                       -1, -1
7350   },
7351   {
7352     Xalpha_k,                           TRUE,   FALSE,
7353     EL_CHAR('K'),                       -1, -1
7354   },
7355   {
7356     Xalpha_l,                           TRUE,   FALSE,
7357     EL_CHAR('L'),                       -1, -1
7358   },
7359   {
7360     Xalpha_m,                           TRUE,   FALSE,
7361     EL_CHAR('M'),                       -1, -1
7362   },
7363   {
7364     Xalpha_n,                           TRUE,   FALSE,
7365     EL_CHAR('N'),                       -1, -1
7366   },
7367   {
7368     Xalpha_o,                           TRUE,   FALSE,
7369     EL_CHAR('O'),                       -1, -1
7370   },
7371   {
7372     Xalpha_p,                           TRUE,   FALSE,
7373     EL_CHAR('P'),                       -1, -1
7374   },
7375   {
7376     Xalpha_q,                           TRUE,   FALSE,
7377     EL_CHAR('Q'),                       -1, -1
7378   },
7379   {
7380     Xalpha_r,                           TRUE,   FALSE,
7381     EL_CHAR('R'),                       -1, -1
7382   },
7383   {
7384     Xalpha_s,                           TRUE,   FALSE,
7385     EL_CHAR('S'),                       -1, -1
7386   },
7387   {
7388     Xalpha_t,                           TRUE,   FALSE,
7389     EL_CHAR('T'),                       -1, -1
7390   },
7391   {
7392     Xalpha_u,                           TRUE,   FALSE,
7393     EL_CHAR('U'),                       -1, -1
7394   },
7395   {
7396     Xalpha_v,                           TRUE,   FALSE,
7397     EL_CHAR('V'),                       -1, -1
7398   },
7399   {
7400     Xalpha_w,                           TRUE,   FALSE,
7401     EL_CHAR('W'),                       -1, -1
7402   },
7403   {
7404     Xalpha_x,                           TRUE,   FALSE,
7405     EL_CHAR('X'),                       -1, -1
7406   },
7407   {
7408     Xalpha_y,                           TRUE,   FALSE,
7409     EL_CHAR('Y'),                       -1, -1
7410   },
7411   {
7412     Xalpha_z,                           TRUE,   FALSE,
7413     EL_CHAR('Z'),                       -1, -1
7414   },
7415   {
7416     Xalpha_arrow_e,                     TRUE,   FALSE,
7417     EL_CHAR('>'),                       -1, -1
7418   },
7419   {
7420     Xalpha_arrow_w,                     TRUE,   FALSE,
7421     EL_CHAR('<'),                       -1, -1
7422   },
7423   {
7424     Xalpha_copyr,                       TRUE,   FALSE,
7425     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
7426   },
7427
7428   {
7429     Ykey_1_blank,                       FALSE,  FALSE,
7430     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
7431   },
7432   {
7433     Ykey_2_blank,                       FALSE,  FALSE,
7434     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
7435   },
7436   {
7437     Ykey_3_blank,                       FALSE,  FALSE,
7438     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
7439   },
7440   {
7441     Ykey_4_blank,                       FALSE,  FALSE,
7442     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
7443   },
7444   {
7445     Ykey_5_blank,                       FALSE,  FALSE,
7446     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
7447   },
7448   {
7449     Ykey_6_blank,                       FALSE,  FALSE,
7450     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
7451   },
7452   {
7453     Ykey_7_blank,                       FALSE,  FALSE,
7454     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
7455   },
7456   {
7457     Ykey_8_blank,                       FALSE,  FALSE,
7458     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
7459   },
7460   {
7461     Ylenses_blank,                      FALSE,  FALSE,
7462     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
7463   },
7464   {
7465     Ymagnify_blank,                     FALSE,  FALSE,
7466     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
7467   },
7468   {
7469     Ygrass_blank,                       FALSE,  FALSE,
7470     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
7471   },
7472   {
7473     Ydirt_blank,                        FALSE,  FALSE,
7474     EL_SAND,                            ACTION_SNAPPING, -1
7475   },
7476
7477   {
7478     -1,                                 FALSE,  FALSE,
7479     -1,                                 -1, -1
7480   }
7481 };
7482
7483 static struct Mapping_EM_to_RND_player
7484 {
7485   int action_em;
7486   int player_nr;
7487
7488   int element_rnd;
7489   int action;
7490   int direction;
7491 }
7492 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7493 {
7494   {
7495     PLY_walk_n,                         0,
7496     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
7497   },
7498   {
7499     PLY_walk_e,                         0,
7500     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
7501   },
7502   {
7503     PLY_walk_s,                         0,
7504     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
7505   },
7506   {
7507     PLY_walk_w,                         0,
7508     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
7509   },
7510   {
7511     PLY_push_n,                         0,
7512     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
7513   },
7514   {
7515     PLY_push_e,                         0,
7516     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
7517   },
7518   {
7519     PLY_push_s,                         0,
7520     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
7521   },
7522   {
7523     PLY_push_w,                         0,
7524     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
7525   },
7526   {
7527     PLY_shoot_n,                        0,
7528     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
7529   },
7530   {
7531     PLY_shoot_e,                        0,
7532     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7533   },
7534   {
7535     PLY_shoot_s,                        0,
7536     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
7537   },
7538   {
7539     PLY_shoot_w,                        0,
7540     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
7541   },
7542   {
7543     PLY_walk_n,                         1,
7544     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
7545   },
7546   {
7547     PLY_walk_e,                         1,
7548     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
7549   },
7550   {
7551     PLY_walk_s,                         1,
7552     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
7553   },
7554   {
7555     PLY_walk_w,                         1,
7556     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
7557   },
7558   {
7559     PLY_push_n,                         1,
7560     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
7561   },
7562   {
7563     PLY_push_e,                         1,
7564     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
7565   },
7566   {
7567     PLY_push_s,                         1,
7568     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
7569   },
7570   {
7571     PLY_push_w,                         1,
7572     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
7573   },
7574   {
7575     PLY_shoot_n,                        1,
7576     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
7577   },
7578   {
7579     PLY_shoot_e,                        1,
7580     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7581   },
7582   {
7583     PLY_shoot_s,                        1,
7584     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
7585   },
7586   {
7587     PLY_shoot_w,                        1,
7588     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
7589   },
7590   {
7591     PLY_still,                          0,
7592     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
7593   },
7594   {
7595     PLY_still,                          1,
7596     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
7597   },
7598   {
7599     PLY_walk_n,                         2,
7600     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
7601   },
7602   {
7603     PLY_walk_e,                         2,
7604     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
7605   },
7606   {
7607     PLY_walk_s,                         2,
7608     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
7609   },
7610   {
7611     PLY_walk_w,                         2,
7612     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
7613   },
7614   {
7615     PLY_push_n,                         2,
7616     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
7617   },
7618   {
7619     PLY_push_e,                         2,
7620     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
7621   },
7622   {
7623     PLY_push_s,                         2,
7624     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
7625   },
7626   {
7627     PLY_push_w,                         2,
7628     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
7629   },
7630   {
7631     PLY_shoot_n,                        2,
7632     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
7633   },
7634   {
7635     PLY_shoot_e,                        2,
7636     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7637   },
7638   {
7639     PLY_shoot_s,                        2,
7640     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
7641   },
7642   {
7643     PLY_shoot_w,                        2,
7644     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
7645   },
7646   {
7647     PLY_walk_n,                         3,
7648     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
7649   },
7650   {
7651     PLY_walk_e,                         3,
7652     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
7653   },
7654   {
7655     PLY_walk_s,                         3,
7656     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
7657   },
7658   {
7659     PLY_walk_w,                         3,
7660     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
7661   },
7662   {
7663     PLY_push_n,                         3,
7664     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
7665   },
7666   {
7667     PLY_push_e,                         3,
7668     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
7669   },
7670   {
7671     PLY_push_s,                         3,
7672     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
7673   },
7674   {
7675     PLY_push_w,                         3,
7676     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
7677   },
7678   {
7679     PLY_shoot_n,                        3,
7680     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
7681   },
7682   {
7683     PLY_shoot_e,                        3,
7684     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7685   },
7686   {
7687     PLY_shoot_s,                        3,
7688     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
7689   },
7690   {
7691     PLY_shoot_w,                        3,
7692     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
7693   },
7694   {
7695     PLY_still,                          2,
7696     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
7697   },
7698   {
7699     PLY_still,                          3,
7700     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7701   },
7702
7703   {
7704     -1,                                 -1,
7705     -1,                                 -1, -1
7706   }
7707 };
7708
7709 int map_element_RND_to_EM_cave(int element_rnd)
7710 {
7711   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7712   static boolean mapping_initialized = FALSE;
7713
7714   if (!mapping_initialized)
7715   {
7716     int i;
7717
7718     // return "Xalpha_quest" for all undefined elements in mapping array
7719     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7720       mapping_RND_to_EM[i] = Xalpha_quest;
7721
7722     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7723       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7724         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7725           em_object_mapping_list[i].element_em;
7726
7727     mapping_initialized = TRUE;
7728   }
7729
7730   if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7731   {
7732     Warn("invalid RND level element %d", element_rnd);
7733
7734     return EL_UNKNOWN;
7735   }
7736
7737   return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7738 }
7739
7740 int map_element_EM_to_RND_cave(int element_em_cave)
7741 {
7742   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7743   static boolean mapping_initialized = FALSE;
7744
7745   if (!mapping_initialized)
7746   {
7747     int i;
7748
7749     // return "EL_UNKNOWN" for all undefined elements in mapping array
7750     for (i = 0; i < GAME_TILE_MAX; i++)
7751       mapping_EM_to_RND[i] = EL_UNKNOWN;
7752
7753     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7754       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7755         em_object_mapping_list[i].element_rnd;
7756
7757     mapping_initialized = TRUE;
7758   }
7759
7760   if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7761   {
7762     Warn("invalid EM cave element %d", element_em_cave);
7763
7764     return EL_UNKNOWN;
7765   }
7766
7767   return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7768 }
7769
7770 int map_element_EM_to_RND_game(int element_em_game)
7771 {
7772   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7773   static boolean mapping_initialized = FALSE;
7774
7775   if (!mapping_initialized)
7776   {
7777     int i;
7778
7779     // return "EL_UNKNOWN" for all undefined elements in mapping array
7780     for (i = 0; i < GAME_TILE_MAX; i++)
7781       mapping_EM_to_RND[i] = EL_UNKNOWN;
7782
7783     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7784       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7785         em_object_mapping_list[i].element_rnd;
7786
7787     mapping_initialized = TRUE;
7788   }
7789
7790   if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7791   {
7792     Warn("invalid EM game element %d", element_em_game);
7793
7794     return EL_UNKNOWN;
7795   }
7796
7797   return mapping_EM_to_RND[element_em_game];
7798 }
7799
7800 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7801 {
7802   struct LevelInfo_EM *level_em = level->native_em_level;
7803   struct CAVE *cav = level_em->cav;
7804   int i, j;
7805
7806   for (i = 0; i < GAME_TILE_MAX; i++)
7807     cav->android_array[i] = Cblank;
7808
7809   for (i = 0; i < level->num_android_clone_elements; i++)
7810   {
7811     int element_rnd = level->android_clone_element[i];
7812     int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7813
7814     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7815       if (em_object_mapping_list[j].element_rnd == element_rnd)
7816         cav->android_array[em_object_mapping_list[j].element_em] =
7817           element_em_cave;
7818   }
7819 }
7820
7821 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7822 {
7823   struct LevelInfo_EM *level_em = level->native_em_level;
7824   struct CAVE *cav = level_em->cav;
7825   int i, j;
7826
7827   level->num_android_clone_elements = 0;
7828
7829   for (i = 0; i < GAME_TILE_MAX; i++)
7830   {
7831     int element_em_cave = cav->android_array[i];
7832     int element_rnd;
7833     boolean element_found = FALSE;
7834
7835     if (element_em_cave == Cblank)
7836       continue;
7837
7838     element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7839
7840     for (j = 0; j < level->num_android_clone_elements; j++)
7841       if (level->android_clone_element[j] == element_rnd)
7842         element_found = TRUE;
7843
7844     if (!element_found)
7845     {
7846       level->android_clone_element[level->num_android_clone_elements++] =
7847         element_rnd;
7848
7849       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7850         break;
7851     }
7852   }
7853
7854   if (level->num_android_clone_elements == 0)
7855   {
7856     level->num_android_clone_elements = 1;
7857     level->android_clone_element[0] = EL_EMPTY;
7858   }
7859 }
7860
7861 int map_direction_RND_to_EM(int direction)
7862 {
7863   return (direction == MV_UP    ? 0 :
7864           direction == MV_RIGHT ? 1 :
7865           direction == MV_DOWN  ? 2 :
7866           direction == MV_LEFT  ? 3 :
7867           -1);
7868 }
7869
7870 int map_direction_EM_to_RND(int direction)
7871 {
7872   return (direction == 0 ? MV_UP    :
7873           direction == 1 ? MV_RIGHT :
7874           direction == 2 ? MV_DOWN  :
7875           direction == 3 ? MV_LEFT  :
7876           MV_NONE);
7877 }
7878
7879 int map_element_RND_to_SP(int element_rnd)
7880 {
7881   int element_sp = 0x20;        // map unknown elements to yellow "hardware"
7882
7883   if (element_rnd >= EL_SP_START &&
7884       element_rnd <= EL_SP_END)
7885     element_sp = element_rnd - EL_SP_START;
7886   else if (element_rnd == EL_EMPTY_SPACE)
7887     element_sp = 0x00;
7888   else if (element_rnd == EL_INVISIBLE_WALL)
7889     element_sp = 0x28;
7890
7891   return element_sp;
7892 }
7893
7894 int map_element_SP_to_RND(int element_sp)
7895 {
7896   int element_rnd = EL_UNKNOWN;
7897
7898   if (element_sp >= 0x00 &&
7899       element_sp <= 0x27)
7900     element_rnd = EL_SP_START + element_sp;
7901   else if (element_sp == 0x28)
7902     element_rnd = EL_INVISIBLE_WALL;
7903
7904   return element_rnd;
7905 }
7906
7907 int map_action_SP_to_RND(int action_sp)
7908 {
7909   switch (action_sp)
7910   {
7911     case actActive:             return ACTION_ACTIVE;
7912     case actImpact:             return ACTION_IMPACT;
7913     case actExploding:          return ACTION_EXPLODING;
7914     case actDigging:            return ACTION_DIGGING;
7915     case actSnapping:           return ACTION_SNAPPING;
7916     case actCollecting:         return ACTION_COLLECTING;
7917     case actPassing:            return ACTION_PASSING;
7918     case actPushing:            return ACTION_PUSHING;
7919     case actDropping:           return ACTION_DROPPING;
7920
7921     default:                    return ACTION_DEFAULT;
7922   }
7923 }
7924
7925 int map_element_RND_to_MM(int element_rnd)
7926 {
7927   return (element_rnd >= EL_MM_START_1 &&
7928           element_rnd <= EL_MM_END_1 ?
7929           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7930
7931           element_rnd >= EL_MM_START_2 &&
7932           element_rnd <= EL_MM_END_2 ?
7933           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7934
7935           element_rnd >= EL_CHAR_START &&
7936           element_rnd <= EL_CHAR_END ?
7937           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7938
7939           element_rnd >= EL_MM_RUNTIME_START &&
7940           element_rnd <= EL_MM_RUNTIME_END ?
7941           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7942
7943           element_rnd >= EL_MM_DUMMY_START &&
7944           element_rnd <= EL_MM_DUMMY_END ?
7945           EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7946
7947           EL_MM_EMPTY_NATIVE);
7948 }
7949
7950 int map_element_MM_to_RND(int element_mm)
7951 {
7952   return (element_mm == EL_MM_EMPTY_NATIVE ||
7953           element_mm == EL_DF_EMPTY_NATIVE ?
7954           EL_EMPTY :
7955
7956           element_mm >= EL_MM_START_1_NATIVE &&
7957           element_mm <= EL_MM_END_1_NATIVE ?
7958           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7959
7960           element_mm >= EL_MM_START_2_NATIVE &&
7961           element_mm <= EL_MM_END_2_NATIVE ?
7962           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7963
7964           element_mm >= EL_MM_CHAR_START_NATIVE &&
7965           element_mm <= EL_MM_CHAR_END_NATIVE ?
7966           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7967
7968           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7969           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7970           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7971
7972           element_mm >= EL_MM_DUMMY_START_NATIVE &&
7973           element_mm <= EL_MM_DUMMY_END_NATIVE ?
7974           EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7975
7976           EL_EMPTY);
7977 }
7978
7979 int map_action_MM_to_RND(int action_mm)
7980 {
7981   // all MM actions are defined to exactly match their RND counterparts
7982   return action_mm;
7983 }
7984
7985 int map_sound_MM_to_RND(int sound_mm)
7986 {
7987   switch (sound_mm)
7988   {
7989     case SND_MM_GAME_LEVELTIME_CHARGING:
7990       return SND_GAME_LEVELTIME_CHARGING;
7991
7992     case SND_MM_GAME_HEALTH_CHARGING:
7993       return SND_GAME_HEALTH_CHARGING;
7994
7995     default:
7996       return SND_UNDEFINED;
7997   }
7998 }
7999
8000 int map_mm_wall_element(int element)
8001 {
8002   return (element >= EL_MM_STEEL_WALL_START &&
8003           element <= EL_MM_STEEL_WALL_END ?
8004           EL_MM_STEEL_WALL :
8005
8006           element >= EL_MM_WOODEN_WALL_START &&
8007           element <= EL_MM_WOODEN_WALL_END ?
8008           EL_MM_WOODEN_WALL :
8009
8010           element >= EL_MM_ICE_WALL_START &&
8011           element <= EL_MM_ICE_WALL_END ?
8012           EL_MM_ICE_WALL :
8013
8014           element >= EL_MM_AMOEBA_WALL_START &&
8015           element <= EL_MM_AMOEBA_WALL_END ?
8016           EL_MM_AMOEBA_WALL :
8017
8018           element >= EL_DF_STEEL_WALL_START &&
8019           element <= EL_DF_STEEL_WALL_END ?
8020           EL_DF_STEEL_WALL :
8021
8022           element >= EL_DF_WOODEN_WALL_START &&
8023           element <= EL_DF_WOODEN_WALL_END ?
8024           EL_DF_WOODEN_WALL :
8025
8026           element);
8027 }
8028
8029 int map_mm_wall_element_editor(int element)
8030 {
8031   switch (element)
8032   {
8033     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
8034     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
8035     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
8036     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
8037     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
8038     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
8039
8040     default:                    return element;
8041   }
8042 }
8043
8044 int get_next_element(int element)
8045 {
8046   switch (element)
8047   {
8048     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
8049     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
8050     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
8051     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
8052     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
8053     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
8054     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
8055     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
8056     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
8057     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
8058     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
8059
8060     default:                            return element;
8061   }
8062 }
8063
8064 int el2img_mm(int element_mm)
8065 {
8066   return el2img(map_element_MM_to_RND(element_mm));
8067 }
8068
8069 int el_act_dir2img(int element, int action, int direction)
8070 {
8071   element = GFX_ELEMENT(element);
8072   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8073
8074   // direction_graphic[][] == graphic[] for undefined direction graphics
8075   return element_info[element].direction_graphic[action][direction];
8076 }
8077
8078 static int el_act_dir2crm(int element, int action, int direction)
8079 {
8080   element = GFX_ELEMENT(element);
8081   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8082
8083   // direction_graphic[][] == graphic[] for undefined direction graphics
8084   return element_info[element].direction_crumbled[action][direction];
8085 }
8086
8087 int el_act2img(int element, int action)
8088 {
8089   element = GFX_ELEMENT(element);
8090
8091   return element_info[element].graphic[action];
8092 }
8093
8094 int el_act2crm(int element, int action)
8095 {
8096   element = GFX_ELEMENT(element);
8097
8098   return element_info[element].crumbled[action];
8099 }
8100
8101 int el_dir2img(int element, int direction)
8102 {
8103   element = GFX_ELEMENT(element);
8104
8105   return el_act_dir2img(element, ACTION_DEFAULT, direction);
8106 }
8107
8108 int el2baseimg(int element)
8109 {
8110   return element_info[element].graphic[ACTION_DEFAULT];
8111 }
8112
8113 int el2img(int element)
8114 {
8115   element = GFX_ELEMENT(element);
8116
8117   return element_info[element].graphic[ACTION_DEFAULT];
8118 }
8119
8120 int el2edimg(int element)
8121 {
8122   element = GFX_ELEMENT(element);
8123
8124   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8125 }
8126
8127 int el2preimg(int element)
8128 {
8129   element = GFX_ELEMENT(element);
8130
8131   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8132 }
8133
8134 int el2panelimg(int element)
8135 {
8136   element = GFX_ELEMENT(element);
8137
8138   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8139 }
8140
8141 int font2baseimg(int font_nr)
8142 {
8143   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8144 }
8145
8146 int getBeltNrFromBeltElement(int element)
8147 {
8148   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8149           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8150           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8151 }
8152
8153 int getBeltNrFromBeltActiveElement(int element)
8154 {
8155   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8156           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8157           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8158 }
8159
8160 int getBeltNrFromBeltSwitchElement(int element)
8161 {
8162   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8163           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8164           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8165 }
8166
8167 int getBeltDirNrFromBeltElement(int element)
8168 {
8169   static int belt_base_element[4] =
8170   {
8171     EL_CONVEYOR_BELT_1_LEFT,
8172     EL_CONVEYOR_BELT_2_LEFT,
8173     EL_CONVEYOR_BELT_3_LEFT,
8174     EL_CONVEYOR_BELT_4_LEFT
8175   };
8176
8177   int belt_nr = getBeltNrFromBeltElement(element);
8178   int belt_dir_nr = element - belt_base_element[belt_nr];
8179
8180   return (belt_dir_nr % 3);
8181 }
8182
8183 int getBeltDirNrFromBeltSwitchElement(int element)
8184 {
8185   static int belt_base_element[4] =
8186   {
8187     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8188     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8189     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8190     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8191   };
8192
8193   int belt_nr = getBeltNrFromBeltSwitchElement(element);
8194   int belt_dir_nr = element - belt_base_element[belt_nr];
8195
8196   return (belt_dir_nr % 3);
8197 }
8198
8199 int getBeltDirFromBeltElement(int element)
8200 {
8201   static int belt_move_dir[3] =
8202   {
8203     MV_LEFT,
8204     MV_NONE,
8205     MV_RIGHT
8206   };
8207
8208   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8209
8210   return belt_move_dir[belt_dir_nr];
8211 }
8212
8213 int getBeltDirFromBeltSwitchElement(int element)
8214 {
8215   static int belt_move_dir[3] =
8216   {
8217     MV_LEFT,
8218     MV_NONE,
8219     MV_RIGHT
8220   };
8221
8222   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8223
8224   return belt_move_dir[belt_dir_nr];
8225 }
8226
8227 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8228 {
8229   static int belt_base_element[4] =
8230   {
8231     EL_CONVEYOR_BELT_1_LEFT,
8232     EL_CONVEYOR_BELT_2_LEFT,
8233     EL_CONVEYOR_BELT_3_LEFT,
8234     EL_CONVEYOR_BELT_4_LEFT
8235   };
8236
8237   return belt_base_element[belt_nr] + belt_dir_nr;
8238 }
8239
8240 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8241 {
8242   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8243
8244   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8245 }
8246
8247 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8248 {
8249   static int belt_base_element[4] =
8250   {
8251     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8252     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8253     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8254     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8255   };
8256
8257   return belt_base_element[belt_nr] + belt_dir_nr;
8258 }
8259
8260 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8261 {
8262   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8263
8264   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8265 }
8266
8267 boolean swapTiles_EM(boolean is_pre_emc_cave)
8268 {
8269   return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8270 }
8271
8272 boolean getTeamMode_EM(void)
8273 {
8274   return game.team_mode || network_playing;
8275 }
8276
8277 boolean isActivePlayer_EM(int player_nr)
8278 {
8279   return stored_player[player_nr].active;
8280 }
8281
8282 unsigned int InitRND(int seed)
8283 {
8284   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8285     return InitEngineRandom_EM(seed);
8286   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8287     return InitEngineRandom_SP(seed);
8288   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8289     return InitEngineRandom_MM(seed);
8290   else
8291     return InitEngineRandom_RND(seed);
8292 }
8293
8294 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8295 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8296
8297 static int get_effective_element_EM(int tile, int frame_em)
8298 {
8299   int element             = object_mapping[tile].element_rnd;
8300   int action              = object_mapping[tile].action;
8301   boolean is_backside     = object_mapping[tile].is_backside;
8302   boolean action_removing = (action == ACTION_DIGGING ||
8303                              action == ACTION_SNAPPING ||
8304                              action == ACTION_COLLECTING);
8305
8306   if (frame_em < 7)
8307   {
8308     switch (tile)
8309     {
8310       case Xsplash_e:
8311       case Xsplash_w:
8312         return (frame_em > 5 ? EL_EMPTY : element);
8313
8314       default:
8315         return element;
8316     }
8317   }
8318   else  // frame_em == 7
8319   {
8320     switch (tile)
8321     {
8322       case Xsplash_e:
8323       case Xsplash_w:
8324         return EL_EMPTY;
8325
8326       case Ynut_stone:
8327         return EL_EMERALD;
8328
8329       case Ydiamond_stone:
8330         return EL_ROCK;
8331
8332       case Xdrip_stretch:
8333       case Xdrip_stretchB:
8334       case Ydrip_1_s:
8335       case Ydrip_1_sB:
8336       case Yball_1:
8337       case Xball_2:
8338       case Yball_2:
8339       case Yball_blank:
8340       case Ykey_1_blank:
8341       case Ykey_2_blank:
8342       case Ykey_3_blank:
8343       case Ykey_4_blank:
8344       case Ykey_5_blank:
8345       case Ykey_6_blank:
8346       case Ykey_7_blank:
8347       case Ykey_8_blank:
8348       case Ylenses_blank:
8349       case Ymagnify_blank:
8350       case Ygrass_blank:
8351       case Ydirt_blank:
8352       case Xsand_stonein_1:
8353       case Xsand_stonein_2:
8354       case Xsand_stonein_3:
8355       case Xsand_stonein_4:
8356         return element;
8357
8358       default:
8359         return (is_backside || action_removing ? EL_EMPTY : element);
8360     }
8361   }
8362 }
8363
8364 static boolean check_linear_animation_EM(int tile)
8365 {
8366   switch (tile)
8367   {
8368     case Xsand_stonesand_1:
8369     case Xsand_stonesand_quickout_1:
8370     case Xsand_sandstone_1:
8371     case Xsand_stonein_1:
8372     case Xsand_stoneout_1:
8373     case Xboom_1:
8374     case Xdynamite_1:
8375     case Ybug_w_n:
8376     case Ybug_n_e:
8377     case Ybug_e_s:
8378     case Ybug_s_w:
8379     case Ybug_e_n:
8380     case Ybug_s_e:
8381     case Ybug_w_s:
8382     case Ybug_n_w:
8383     case Ytank_w_n:
8384     case Ytank_n_e:
8385     case Ytank_e_s:
8386     case Ytank_s_w:
8387     case Ytank_e_n:
8388     case Ytank_s_e:
8389     case Ytank_w_s:
8390     case Ytank_n_w:
8391     case Xsplash_e:
8392     case Xsplash_w:
8393     case Ynut_stone:
8394       return TRUE;
8395   }
8396
8397   return FALSE;
8398 }
8399
8400 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8401                                      boolean has_crumbled_graphics,
8402                                      int crumbled, int sync_frame)
8403 {
8404   // if element can be crumbled, but certain action graphics are just empty
8405   // space (like instantly snapping sand to empty space in 1 frame), do not
8406   // treat these empty space graphics as crumbled graphics in EMC engine
8407   if (crumbled == IMG_EMPTY_SPACE)
8408     has_crumbled_graphics = FALSE;
8409
8410   if (has_crumbled_graphics)
8411   {
8412     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8413     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8414                                            g_crumbled->anim_delay,
8415                                            g_crumbled->anim_mode,
8416                                            g_crumbled->anim_start_frame,
8417                                            sync_frame);
8418
8419     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8420                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8421
8422     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8423     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8424
8425     g_em->has_crumbled_graphics = TRUE;
8426   }
8427   else
8428   {
8429     g_em->crumbled_bitmap = NULL;
8430     g_em->crumbled_src_x = 0;
8431     g_em->crumbled_src_y = 0;
8432     g_em->crumbled_border_size = 0;
8433     g_em->crumbled_tile_size = 0;
8434
8435     g_em->has_crumbled_graphics = FALSE;
8436   }
8437 }
8438
8439 #if 0
8440 void ResetGfxAnimation_EM(int x, int y, int tile)
8441 {
8442   GfxFrame[x][y] = 0;
8443 }
8444 #endif
8445
8446 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8447                         int tile, int frame_em, int x, int y)
8448 {
8449   int action = object_mapping[tile].action;
8450   int direction = object_mapping[tile].direction;
8451   int effective_element = get_effective_element_EM(tile, frame_em);
8452   int graphic = (direction == MV_NONE ?
8453                  el_act2img(effective_element, action) :
8454                  el_act_dir2img(effective_element, action, direction));
8455   struct GraphicInfo *g = &graphic_info[graphic];
8456   int sync_frame;
8457   boolean action_removing = (action == ACTION_DIGGING ||
8458                              action == ACTION_SNAPPING ||
8459                              action == ACTION_COLLECTING);
8460   boolean action_moving   = (action == ACTION_FALLING ||
8461                              action == ACTION_MOVING ||
8462                              action == ACTION_PUSHING ||
8463                              action == ACTION_EATING ||
8464                              action == ACTION_FILLING ||
8465                              action == ACTION_EMPTYING);
8466   boolean action_falling  = (action == ACTION_FALLING ||
8467                              action == ACTION_FILLING ||
8468                              action == ACTION_EMPTYING);
8469
8470   // special case: graphic uses "2nd movement tile" and has defined
8471   // 7 frames for movement animation (or less) => use default graphic
8472   // for last (8th) frame which ends the movement animation
8473   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8474   {
8475     action = ACTION_DEFAULT;    // (keep action_* unchanged for now)
8476     graphic = (direction == MV_NONE ?
8477                el_act2img(effective_element, action) :
8478                el_act_dir2img(effective_element, action, direction));
8479
8480     g = &graphic_info[graphic];
8481   }
8482
8483   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8484   {
8485     GfxFrame[x][y] = 0;
8486   }
8487   else if (action_moving)
8488   {
8489     boolean is_backside = object_mapping[tile].is_backside;
8490
8491     if (is_backside)
8492     {
8493       int direction = object_mapping[tile].direction;
8494       int move_dir = (action_falling ? MV_DOWN : direction);
8495
8496       GfxFrame[x][y]++;
8497
8498 #if 1
8499       // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8500       if (g->double_movement && frame_em == 0)
8501         GfxFrame[x][y] = 0;
8502 #endif
8503
8504       if (move_dir == MV_LEFT)
8505         GfxFrame[x - 1][y] = GfxFrame[x][y];
8506       else if (move_dir == MV_RIGHT)
8507         GfxFrame[x + 1][y] = GfxFrame[x][y];
8508       else if (move_dir == MV_UP)
8509         GfxFrame[x][y - 1] = GfxFrame[x][y];
8510       else if (move_dir == MV_DOWN)
8511         GfxFrame[x][y + 1] = GfxFrame[x][y];
8512     }
8513   }
8514   else
8515   {
8516     GfxFrame[x][y]++;
8517
8518     // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8519     if (tile == Xsand_stonesand_quickout_1 ||
8520         tile == Xsand_stonesand_quickout_2)
8521       GfxFrame[x][y]++;
8522   }
8523
8524   if (graphic_info[graphic].anim_global_sync)
8525     sync_frame = FrameCounter;
8526   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8527     sync_frame = GfxFrame[x][y];
8528   else
8529     sync_frame = 0;     // playfield border (pseudo steel)
8530
8531   SetRandomAnimationValue(x, y);
8532
8533   int frame = getAnimationFrame(g->anim_frames,
8534                                 g->anim_delay,
8535                                 g->anim_mode,
8536                                 g->anim_start_frame,
8537                                 sync_frame);
8538
8539   g_em->unique_identifier =
8540     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8541 }
8542
8543 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8544                                   int tile, int frame_em, int x, int y)
8545 {
8546   int action = object_mapping[tile].action;
8547   int direction = object_mapping[tile].direction;
8548   boolean is_backside = object_mapping[tile].is_backside;
8549   int effective_element = get_effective_element_EM(tile, frame_em);
8550   int effective_action = action;
8551   int graphic = (direction == MV_NONE ?
8552                  el_act2img(effective_element, effective_action) :
8553                  el_act_dir2img(effective_element, effective_action,
8554                                 direction));
8555   int crumbled = (direction == MV_NONE ?
8556                   el_act2crm(effective_element, effective_action) :
8557                   el_act_dir2crm(effective_element, effective_action,
8558                                  direction));
8559   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8560   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8561   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8562   struct GraphicInfo *g = &graphic_info[graphic];
8563   int sync_frame;
8564
8565   // special case: graphic uses "2nd movement tile" and has defined
8566   // 7 frames for movement animation (or less) => use default graphic
8567   // for last (8th) frame which ends the movement animation
8568   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8569   {
8570     effective_action = ACTION_DEFAULT;
8571     graphic = (direction == MV_NONE ?
8572                el_act2img(effective_element, effective_action) :
8573                el_act_dir2img(effective_element, effective_action,
8574                               direction));
8575     crumbled = (direction == MV_NONE ?
8576                 el_act2crm(effective_element, effective_action) :
8577                 el_act_dir2crm(effective_element, effective_action,
8578                                direction));
8579
8580     g = &graphic_info[graphic];
8581   }
8582
8583   if (graphic_info[graphic].anim_global_sync)
8584     sync_frame = FrameCounter;
8585   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8586     sync_frame = GfxFrame[x][y];
8587   else
8588     sync_frame = 0;     // playfield border (pseudo steel)
8589
8590   SetRandomAnimationValue(x, y);
8591
8592   int frame = getAnimationFrame(g->anim_frames,
8593                                 g->anim_delay,
8594                                 g->anim_mode,
8595                                 g->anim_start_frame,
8596                                 sync_frame);
8597
8598   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8599                       g->double_movement && is_backside);
8600
8601   // (updating the "crumbled" graphic definitions is probably not really needed,
8602   // as animations for crumbled graphics can't be longer than one EMC cycle)
8603   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8604                            sync_frame);
8605 }
8606
8607 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8608                                   int player_nr, int anim, int frame_em)
8609 {
8610   int element   = player_mapping[player_nr][anim].element_rnd;
8611   int action    = player_mapping[player_nr][anim].action;
8612   int direction = player_mapping[player_nr][anim].direction;
8613   int graphic = (direction == MV_NONE ?
8614                  el_act2img(element, action) :
8615                  el_act_dir2img(element, action, direction));
8616   struct GraphicInfo *g = &graphic_info[graphic];
8617   int sync_frame;
8618
8619   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8620
8621   stored_player[player_nr].StepFrame = frame_em;
8622
8623   sync_frame = stored_player[player_nr].Frame;
8624
8625   int frame = getAnimationFrame(g->anim_frames,
8626                                 g->anim_delay,
8627                                 g->anim_mode,
8628                                 g->anim_start_frame,
8629                                 sync_frame);
8630
8631   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8632                       &g_em->src_x, &g_em->src_y, FALSE);
8633 }
8634
8635 void InitGraphicInfo_EM(void)
8636 {
8637   int i, j, p;
8638
8639   // always start with reliable default values
8640   for (i = 0; i < GAME_TILE_MAX; i++)
8641   {
8642     object_mapping[i].element_rnd = EL_UNKNOWN;
8643     object_mapping[i].is_backside = FALSE;
8644     object_mapping[i].action = ACTION_DEFAULT;
8645     object_mapping[i].direction = MV_NONE;
8646   }
8647
8648   // always start with reliable default values
8649   for (p = 0; p < MAX_PLAYERS; p++)
8650   {
8651     for (i = 0; i < PLY_MAX; i++)
8652     {
8653       player_mapping[p][i].element_rnd = EL_UNKNOWN;
8654       player_mapping[p][i].action = ACTION_DEFAULT;
8655       player_mapping[p][i].direction = MV_NONE;
8656     }
8657   }
8658
8659   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8660   {
8661     int e = em_object_mapping_list[i].element_em;
8662
8663     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8664     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8665
8666     if (em_object_mapping_list[i].action != -1)
8667       object_mapping[e].action = em_object_mapping_list[i].action;
8668
8669     if (em_object_mapping_list[i].direction != -1)
8670       object_mapping[e].direction =
8671         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8672   }
8673
8674   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8675   {
8676     int a = em_player_mapping_list[i].action_em;
8677     int p = em_player_mapping_list[i].player_nr;
8678
8679     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8680
8681     if (em_player_mapping_list[i].action != -1)
8682       player_mapping[p][a].action = em_player_mapping_list[i].action;
8683
8684     if (em_player_mapping_list[i].direction != -1)
8685       player_mapping[p][a].direction =
8686         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8687   }
8688
8689   for (i = 0; i < GAME_TILE_MAX; i++)
8690   {
8691     int element = object_mapping[i].element_rnd;
8692     int action = object_mapping[i].action;
8693     int direction = object_mapping[i].direction;
8694     boolean is_backside = object_mapping[i].is_backside;
8695     boolean action_exploding = ((action == ACTION_EXPLODING ||
8696                                  action == ACTION_SMASHED_BY_ROCK ||
8697                                  action == ACTION_SMASHED_BY_SPRING) &&
8698                                 element != EL_DIAMOND);
8699     boolean action_active = (action == ACTION_ACTIVE);
8700     boolean action_other = (action == ACTION_OTHER);
8701
8702     for (j = 0; j < 8; j++)
8703     {
8704       int effective_element = get_effective_element_EM(i, j);
8705       int effective_action = (j < 7 ? action :
8706                               i == Xdrip_stretch ? action :
8707                               i == Xdrip_stretchB ? action :
8708                               i == Ydrip_1_s ? action :
8709                               i == Ydrip_1_sB ? action :
8710                               i == Yball_1 ? action :
8711                               i == Xball_2 ? action :
8712                               i == Yball_2 ? action :
8713                               i == Yball_blank ? action :
8714                               i == Ykey_1_blank ? action :
8715                               i == Ykey_2_blank ? action :
8716                               i == Ykey_3_blank ? action :
8717                               i == Ykey_4_blank ? action :
8718                               i == Ykey_5_blank ? action :
8719                               i == Ykey_6_blank ? action :
8720                               i == Ykey_7_blank ? action :
8721                               i == Ykey_8_blank ? action :
8722                               i == Ylenses_blank ? action :
8723                               i == Ymagnify_blank ? action :
8724                               i == Ygrass_blank ? action :
8725                               i == Ydirt_blank ? action :
8726                               i == Xsand_stonein_1 ? action :
8727                               i == Xsand_stonein_2 ? action :
8728                               i == Xsand_stonein_3 ? action :
8729                               i == Xsand_stonein_4 ? action :
8730                               i == Xsand_stoneout_1 ? action :
8731                               i == Xsand_stoneout_2 ? action :
8732                               i == Xboom_android ? ACTION_EXPLODING :
8733                               action_exploding ? ACTION_EXPLODING :
8734                               action_active ? action :
8735                               action_other ? action :
8736                               ACTION_DEFAULT);
8737       int graphic = (el_act_dir2img(effective_element, effective_action,
8738                                     direction));
8739       int crumbled = (el_act_dir2crm(effective_element, effective_action,
8740                                      direction));
8741       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8742       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8743       boolean has_action_graphics = (graphic != base_graphic);
8744       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8745       struct GraphicInfo *g = &graphic_info[graphic];
8746       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8747       Bitmap *src_bitmap;
8748       int src_x, src_y;
8749       // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8750       boolean special_animation = (action != ACTION_DEFAULT &&
8751                                    g->anim_frames == 3 &&
8752                                    g->anim_delay == 2 &&
8753                                    g->anim_mode & ANIM_LINEAR);
8754       int sync_frame = (i == Xdrip_stretch ? 7 :
8755                         i == Xdrip_stretchB ? 7 :
8756                         i == Ydrip_2_s ? j + 8 :
8757                         i == Ydrip_2_sB ? j + 8 :
8758                         i == Xacid_1 ? 0 :
8759                         i == Xacid_2 ? 10 :
8760                         i == Xacid_3 ? 20 :
8761                         i == Xacid_4 ? 30 :
8762                         i == Xacid_5 ? 40 :
8763                         i == Xacid_6 ? 50 :
8764                         i == Xacid_7 ? 60 :
8765                         i == Xacid_8 ? 70 :
8766                         i == Xfake_acid_1 ? 0 :
8767                         i == Xfake_acid_2 ? 10 :
8768                         i == Xfake_acid_3 ? 20 :
8769                         i == Xfake_acid_4 ? 30 :
8770                         i == Xfake_acid_5 ? 40 :
8771                         i == Xfake_acid_6 ? 50 :
8772                         i == Xfake_acid_7 ? 60 :
8773                         i == Xfake_acid_8 ? 70 :
8774                         i == Xfake_acid_1_player ? 0 :
8775                         i == Xfake_acid_2_player ? 10 :
8776                         i == Xfake_acid_3_player ? 20 :
8777                         i == Xfake_acid_4_player ? 30 :
8778                         i == Xfake_acid_5_player ? 40 :
8779                         i == Xfake_acid_6_player ? 50 :
8780                         i == Xfake_acid_7_player ? 60 :
8781                         i == Xfake_acid_8_player ? 70 :
8782                         i == Xball_2 ? 7 :
8783                         i == Yball_2 ? j + 8 :
8784                         i == Yball_blank ? j + 1 :
8785                         i == Ykey_1_blank ? j + 1 :
8786                         i == Ykey_2_blank ? j + 1 :
8787                         i == Ykey_3_blank ? j + 1 :
8788                         i == Ykey_4_blank ? j + 1 :
8789                         i == Ykey_5_blank ? j + 1 :
8790                         i == Ykey_6_blank ? j + 1 :
8791                         i == Ykey_7_blank ? j + 1 :
8792                         i == Ykey_8_blank ? j + 1 :
8793                         i == Ylenses_blank ? j + 1 :
8794                         i == Ymagnify_blank ? j + 1 :
8795                         i == Ygrass_blank ? j + 1 :
8796                         i == Ydirt_blank ? j + 1 :
8797                         i == Xamoeba_1 ? 0 :
8798                         i == Xamoeba_2 ? 1 :
8799                         i == Xamoeba_3 ? 2 :
8800                         i == Xamoeba_4 ? 3 :
8801                         i == Xamoeba_5 ? 0 :
8802                         i == Xamoeba_6 ? 1 :
8803                         i == Xamoeba_7 ? 2 :
8804                         i == Xamoeba_8 ? 3 :
8805                         i == Xexit_2 ? j + 8 :
8806                         i == Xexit_3 ? j + 16 :
8807                         i == Xdynamite_1 ? 0 :
8808                         i == Xdynamite_2 ? 8 :
8809                         i == Xdynamite_3 ? 16 :
8810                         i == Xdynamite_4 ? 24 :
8811                         i == Xsand_stonein_1 ? j + 1 :
8812                         i == Xsand_stonein_2 ? j + 9 :
8813                         i == Xsand_stonein_3 ? j + 17 :
8814                         i == Xsand_stonein_4 ? j + 25 :
8815                         i == Xsand_stoneout_1 && j == 0 ? 0 :
8816                         i == Xsand_stoneout_1 && j == 1 ? 0 :
8817                         i == Xsand_stoneout_1 && j == 2 ? 1 :
8818                         i == Xsand_stoneout_1 && j == 3 ? 2 :
8819                         i == Xsand_stoneout_1 && j == 4 ? 2 :
8820                         i == Xsand_stoneout_1 && j == 5 ? 3 :
8821                         i == Xsand_stoneout_1 && j == 6 ? 4 :
8822                         i == Xsand_stoneout_1 && j == 7 ? 4 :
8823                         i == Xsand_stoneout_2 && j == 0 ? 5 :
8824                         i == Xsand_stoneout_2 && j == 1 ? 6 :
8825                         i == Xsand_stoneout_2 && j == 2 ? 7 :
8826                         i == Xsand_stoneout_2 && j == 3 ? 8 :
8827                         i == Xsand_stoneout_2 && j == 4 ? 9 :
8828                         i == Xsand_stoneout_2 && j == 5 ? 11 :
8829                         i == Xsand_stoneout_2 && j == 6 ? 13 :
8830                         i == Xsand_stoneout_2 && j == 7 ? 15 :
8831                         i == Xboom_bug && j == 1 ? 2 :
8832                         i == Xboom_bug && j == 2 ? 2 :
8833                         i == Xboom_bug && j == 3 ? 4 :
8834                         i == Xboom_bug && j == 4 ? 4 :
8835                         i == Xboom_bug && j == 5 ? 2 :
8836                         i == Xboom_bug && j == 6 ? 2 :
8837                         i == Xboom_bug && j == 7 ? 0 :
8838                         i == Xboom_tank && j == 1 ? 2 :
8839                         i == Xboom_tank && j == 2 ? 2 :
8840                         i == Xboom_tank && j == 3 ? 4 :
8841                         i == Xboom_tank && j == 4 ? 4 :
8842                         i == Xboom_tank && j == 5 ? 2 :
8843                         i == Xboom_tank && j == 6 ? 2 :
8844                         i == Xboom_tank && j == 7 ? 0 :
8845                         i == Xboom_android && j == 7 ? 6 :
8846                         i == Xboom_1 && j == 1 ? 2 :
8847                         i == Xboom_1 && j == 2 ? 2 :
8848                         i == Xboom_1 && j == 3 ? 4 :
8849                         i == Xboom_1 && j == 4 ? 4 :
8850                         i == Xboom_1 && j == 5 ? 6 :
8851                         i == Xboom_1 && j == 6 ? 6 :
8852                         i == Xboom_1 && j == 7 ? 8 :
8853                         i == Xboom_2 && j == 0 ? 8 :
8854                         i == Xboom_2 && j == 1 ? 8 :
8855                         i == Xboom_2 && j == 2 ? 10 :
8856                         i == Xboom_2 && j == 3 ? 10 :
8857                         i == Xboom_2 && j == 4 ? 10 :
8858                         i == Xboom_2 && j == 5 ? 12 :
8859                         i == Xboom_2 && j == 6 ? 12 :
8860                         i == Xboom_2 && j == 7 ? 12 :
8861                         special_animation && j == 4 ? 3 :
8862                         effective_action != action ? 0 :
8863                         j);
8864       int frame = getAnimationFrame(g->anim_frames,
8865                                     g->anim_delay,
8866                                     g->anim_mode,
8867                                     g->anim_start_frame,
8868                                     sync_frame);
8869
8870       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8871                           g->double_movement && is_backside);
8872
8873       g_em->bitmap = src_bitmap;
8874       g_em->src_x = src_x;
8875       g_em->src_y = src_y;
8876       g_em->src_offset_x = 0;
8877       g_em->src_offset_y = 0;
8878       g_em->dst_offset_x = 0;
8879       g_em->dst_offset_y = 0;
8880       g_em->width  = TILEX;
8881       g_em->height = TILEY;
8882
8883       g_em->preserve_background = FALSE;
8884
8885       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8886                                sync_frame);
8887
8888       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8889                                    effective_action == ACTION_MOVING  ||
8890                                    effective_action == ACTION_PUSHING ||
8891                                    effective_action == ACTION_EATING)) ||
8892           (!has_action_graphics && (effective_action == ACTION_FILLING ||
8893                                     effective_action == ACTION_EMPTYING)))
8894       {
8895         int move_dir =
8896           (effective_action == ACTION_FALLING ||
8897            effective_action == ACTION_FILLING ||
8898            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8899         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8900         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
8901         int num_steps = (i == Ydrip_1_s  ? 16 :
8902                          i == Ydrip_1_sB ? 16 :
8903                          i == Ydrip_2_s  ? 16 :
8904                          i == Ydrip_2_sB ? 16 :
8905                          i == Xsand_stonein_1 ? 32 :
8906                          i == Xsand_stonein_2 ? 32 :
8907                          i == Xsand_stonein_3 ? 32 :
8908                          i == Xsand_stonein_4 ? 32 :
8909                          i == Xsand_stoneout_1 ? 16 :
8910                          i == Xsand_stoneout_2 ? 16 : 8);
8911         int cx = ABS(dx) * (TILEX / num_steps);
8912         int cy = ABS(dy) * (TILEY / num_steps);
8913         int step_frame = (i == Ydrip_2_s        ? j + 8 :
8914                           i == Ydrip_2_sB       ? j + 8 :
8915                           i == Xsand_stonein_2  ? j + 8 :
8916                           i == Xsand_stonein_3  ? j + 16 :
8917                           i == Xsand_stonein_4  ? j + 24 :
8918                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8919         int step = (is_backside ? step_frame : num_steps - step_frame);
8920
8921         if (is_backside)        // tile where movement starts
8922         {
8923           if (dx < 0 || dy < 0)
8924           {
8925             g_em->src_offset_x = cx * step;
8926             g_em->src_offset_y = cy * step;
8927           }
8928           else
8929           {
8930             g_em->dst_offset_x = cx * step;
8931             g_em->dst_offset_y = cy * step;
8932           }
8933         }
8934         else                    // tile where movement ends
8935         {
8936           if (dx < 0 || dy < 0)
8937           {
8938             g_em->dst_offset_x = cx * step;
8939             g_em->dst_offset_y = cy * step;
8940           }
8941           else
8942           {
8943             g_em->src_offset_x = cx * step;
8944             g_em->src_offset_y = cy * step;
8945           }
8946         }
8947
8948         g_em->width  = TILEX - cx * step;
8949         g_em->height = TILEY - cy * step;
8950       }
8951
8952       // create unique graphic identifier to decide if tile must be redrawn
8953       /* bit 31 - 16 (16 bit): EM style graphic
8954          bit 15 - 12 ( 4 bit): EM style frame
8955          bit 11 -  6 ( 6 bit): graphic width
8956          bit  5 -  0 ( 6 bit): graphic height */
8957       g_em->unique_identifier =
8958         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8959     }
8960   }
8961
8962   for (i = 0; i < GAME_TILE_MAX; i++)
8963   {
8964     for (j = 0; j < 8; j++)
8965     {
8966       int element = object_mapping[i].element_rnd;
8967       int action = object_mapping[i].action;
8968       int direction = object_mapping[i].direction;
8969       boolean is_backside = object_mapping[i].is_backside;
8970       int graphic_action  = el_act_dir2img(element, action, direction);
8971       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8972
8973       if ((action == ACTION_SMASHED_BY_ROCK ||
8974            action == ACTION_SMASHED_BY_SPRING ||
8975            action == ACTION_EATING) &&
8976           graphic_action == graphic_default)
8977       {
8978         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
8979                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8980                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
8981                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8982                  Xspring);
8983
8984         // no separate animation for "smashed by rock" -- use rock instead
8985         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8986         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8987
8988         g_em->bitmap            = g_xx->bitmap;
8989         g_em->src_x             = g_xx->src_x;
8990         g_em->src_y             = g_xx->src_y;
8991         g_em->src_offset_x      = g_xx->src_offset_x;
8992         g_em->src_offset_y      = g_xx->src_offset_y;
8993         g_em->dst_offset_x      = g_xx->dst_offset_x;
8994         g_em->dst_offset_y      = g_xx->dst_offset_y;
8995         g_em->width             = g_xx->width;
8996         g_em->height            = g_xx->height;
8997         g_em->unique_identifier = g_xx->unique_identifier;
8998
8999         if (!is_backside)
9000           g_em->preserve_background = TRUE;
9001       }
9002     }
9003   }
9004
9005   for (p = 0; p < MAX_PLAYERS; p++)
9006   {
9007     for (i = 0; i < PLY_MAX; i++)
9008     {
9009       int element = player_mapping[p][i].element_rnd;
9010       int action = player_mapping[p][i].action;
9011       int direction = player_mapping[p][i].direction;
9012
9013       for (j = 0; j < 8; j++)
9014       {
9015         int effective_element = element;
9016         int effective_action = action;
9017         int graphic = (direction == MV_NONE ?
9018                        el_act2img(effective_element, effective_action) :
9019                        el_act_dir2img(effective_element, effective_action,
9020                                       direction));
9021         struct GraphicInfo *g = &graphic_info[graphic];
9022         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9023         Bitmap *src_bitmap;
9024         int src_x, src_y;
9025         int sync_frame = j;
9026         int frame = getAnimationFrame(g->anim_frames,
9027                                       g->anim_delay,
9028                                       g->anim_mode,
9029                                       g->anim_start_frame,
9030                                       sync_frame);
9031
9032         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9033
9034         g_em->bitmap = src_bitmap;
9035         g_em->src_x = src_x;
9036         g_em->src_y = src_y;
9037         g_em->src_offset_x = 0;
9038         g_em->src_offset_y = 0;
9039         g_em->dst_offset_x = 0;
9040         g_em->dst_offset_y = 0;
9041         g_em->width  = TILEX;
9042         g_em->height = TILEY;
9043       }
9044     }
9045   }
9046 }
9047
9048 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9049                                        boolean any_player_moving,
9050                                        boolean any_player_snapping,
9051                                        boolean any_player_dropping)
9052 {
9053   if (frame == 7 && !any_player_dropping)
9054   {
9055     if (!local_player->was_waiting)
9056     {
9057       if (!CheckSaveEngineSnapshotToList())
9058         return;
9059
9060       local_player->was_waiting = TRUE;
9061     }
9062   }
9063   else if (any_player_moving || any_player_snapping || any_player_dropping)
9064   {
9065     local_player->was_waiting = FALSE;
9066   }
9067 }
9068
9069 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9070                                        boolean murphy_is_dropping)
9071 {
9072   if (murphy_is_waiting)
9073   {
9074     if (!local_player->was_waiting)
9075     {
9076       if (!CheckSaveEngineSnapshotToList())
9077         return;
9078
9079       local_player->was_waiting = TRUE;
9080     }
9081   }
9082   else
9083   {
9084     local_player->was_waiting = FALSE;
9085   }
9086 }
9087
9088 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9089                                        boolean button_released)
9090 {
9091   if (button_released)
9092   {
9093     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9094       CheckSaveEngineSnapshotToList();
9095   }
9096   else if (element_clicked)
9097   {
9098     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9099       CheckSaveEngineSnapshotToList();
9100
9101     game.snapshot.changed_action = TRUE;
9102   }
9103 }
9104
9105 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9106                                boolean any_player_moving,
9107                                boolean any_player_snapping,
9108                                boolean any_player_dropping)
9109 {
9110   if (tape.single_step && tape.recording && !tape.pausing)
9111     if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9112       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9113
9114   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9115                              any_player_snapping, any_player_dropping);
9116
9117   return tape.pausing;
9118 }
9119
9120 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9121                             boolean murphy_is_dropping)
9122 {
9123   boolean murphy_starts_dropping = FALSE;
9124   int i;
9125
9126   for (i = 0; i < MAX_PLAYERS; i++)
9127     if (stored_player[i].force_dropping)
9128       murphy_starts_dropping = TRUE;
9129
9130   if (tape.single_step && tape.recording && !tape.pausing)
9131     if (murphy_is_waiting && !murphy_starts_dropping)
9132       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9133
9134   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9135 }
9136
9137 void CheckSingleStepMode_MM(boolean element_clicked,
9138                             boolean button_released)
9139 {
9140   if (tape.single_step && tape.recording && !tape.pausing)
9141     if (button_released)
9142       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9143
9144   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9145 }
9146
9147 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9148                          int graphic, int sync_frame, int x, int y)
9149 {
9150   int frame = getGraphicAnimationFrame(graphic, sync_frame);
9151
9152   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9153 }
9154
9155 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9156 {
9157   return (IS_NEXT_FRAME(sync_frame, graphic));
9158 }
9159
9160 int getGraphicInfo_Delay(int graphic)
9161 {
9162   return graphic_info[graphic].anim_delay;
9163 }
9164
9165 void PlayMenuSoundExt(int sound)
9166 {
9167   if (sound == SND_UNDEFINED)
9168     return;
9169
9170   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9171       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9172     return;
9173
9174   if (IS_LOOP_SOUND(sound))
9175     PlaySoundLoop(sound);
9176   else
9177     PlaySound(sound);
9178 }
9179
9180 void PlayMenuSound(void)
9181 {
9182   PlayMenuSoundExt(menu.sound[game_status]);
9183 }
9184
9185 void PlayMenuSoundStereo(int sound, int stereo_position)
9186 {
9187   if (sound == SND_UNDEFINED)
9188     return;
9189
9190   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9191       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9192     return;
9193
9194   if (IS_LOOP_SOUND(sound))
9195     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9196   else
9197     PlaySoundStereo(sound, stereo_position);
9198 }
9199
9200 void PlayMenuSoundIfLoopExt(int sound)
9201 {
9202   if (sound == SND_UNDEFINED)
9203     return;
9204
9205   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9206       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9207     return;
9208
9209   if (IS_LOOP_SOUND(sound))
9210     PlaySoundLoop(sound);
9211 }
9212
9213 void PlayMenuSoundIfLoop(void)
9214 {
9215   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9216 }
9217
9218 void PlayMenuMusicExt(int music)
9219 {
9220   if (music == MUS_UNDEFINED)
9221     return;
9222
9223   if (!setup.sound_music)
9224     return;
9225
9226   if (IS_LOOP_MUSIC(music))
9227     PlayMusicLoop(music);
9228   else
9229     PlayMusic(music);
9230 }
9231
9232 void PlayMenuMusic(void)
9233 {
9234   char *curr_music = getCurrentlyPlayingMusicFilename();
9235   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9236
9237   if (!strEqual(curr_music, next_music))
9238     PlayMenuMusicExt(menu.music[game_status]);
9239 }
9240
9241 void PlayMenuSoundsAndMusic(void)
9242 {
9243   PlayMenuSound();
9244   PlayMenuMusic();
9245 }
9246
9247 static void FadeMenuSounds(void)
9248 {
9249   FadeSounds();
9250 }
9251
9252 static void FadeMenuMusic(void)
9253 {
9254   char *curr_music = getCurrentlyPlayingMusicFilename();
9255   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9256
9257   if (!strEqual(curr_music, next_music))
9258     FadeMusic();
9259 }
9260
9261 void FadeMenuSoundsAndMusic(void)
9262 {
9263   FadeMenuSounds();
9264   FadeMenuMusic();
9265 }
9266
9267 void PlaySoundActivating(void)
9268 {
9269 #if 0
9270   PlaySound(SND_MENU_ITEM_ACTIVATING);
9271 #endif
9272 }
9273
9274 void PlaySoundSelecting(void)
9275 {
9276 #if 0
9277   PlaySound(SND_MENU_ITEM_SELECTING);
9278 #endif
9279 }
9280
9281 void ToggleFullscreenIfNeeded(void)
9282 {
9283   // if setup and video fullscreen state are already matching, nothing do do
9284   if (setup.fullscreen == video.fullscreen_enabled ||
9285       !video.fullscreen_available)
9286     return;
9287
9288   SDLSetWindowFullscreen(setup.fullscreen);
9289
9290   // set setup value according to successfully changed fullscreen mode
9291   setup.fullscreen = video.fullscreen_enabled;
9292 }
9293
9294 void ChangeWindowScalingIfNeeded(void)
9295 {
9296   // if setup and video window scaling are already matching, nothing do do
9297   if (setup.window_scaling_percent == video.window_scaling_percent ||
9298       video.fullscreen_enabled)
9299     return;
9300
9301   SDLSetWindowScaling(setup.window_scaling_percent);
9302
9303   // set setup value according to successfully changed window scaling
9304   setup.window_scaling_percent = video.window_scaling_percent;
9305 }
9306
9307 void ChangeVsyncModeIfNeeded(void)
9308 {
9309   int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9310   int video_vsync_mode = video.vsync_mode;
9311
9312   // if setup and video vsync mode are already matching, nothing do do
9313   if (setup_vsync_mode == video_vsync_mode)
9314     return;
9315
9316   // if renderer is using OpenGL, vsync mode can directly be changed
9317   SDLSetScreenVsyncMode(setup.vsync_mode);
9318
9319   // if vsync mode unchanged, try re-creating renderer to set vsync mode
9320   if (video.vsync_mode == video_vsync_mode)
9321   {
9322     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9323
9324     // save backbuffer content which gets lost when re-creating screen
9325     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9326
9327     // force re-creating screen and renderer to set new vsync mode
9328     video.fullscreen_enabled = !setup.fullscreen;
9329
9330     // when creating new renderer, destroy textures linked to old renderer
9331     FreeAllImageTextures();     // needs old renderer to free the textures
9332
9333     // re-create screen and renderer (including change of vsync mode)
9334     ChangeVideoModeIfNeeded(setup.fullscreen);
9335
9336     // set setup value according to successfully changed fullscreen mode
9337     setup.fullscreen = video.fullscreen_enabled;
9338
9339     // restore backbuffer content from temporary backbuffer backup bitmap
9340     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9341     FreeBitmap(tmp_backbuffer);
9342
9343     // update visible window/screen
9344     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9345
9346     // when changing vsync mode, re-create textures for new renderer
9347     InitImageTextures();
9348   }
9349
9350   // set setup value according to successfully changed vsync mode
9351   setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9352 }
9353
9354 static void JoinRectangles(int *x, int *y, int *width, int *height,
9355                            int x2, int y2, int width2, int height2)
9356 {
9357   // do not join with "off-screen" rectangle
9358   if (x2 == -1 || y2 == -1)
9359     return;
9360
9361   *x = MIN(*x, x2);
9362   *y = MIN(*y, y2);
9363   *width = MAX(*width, width2);
9364   *height = MAX(*height, height2);
9365 }
9366
9367 void SetAnimStatus(int anim_status_new)
9368 {
9369   if (anim_status_new == GAME_MODE_MAIN)
9370     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9371   else if (anim_status_new == GAME_MODE_NAMES)
9372     anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9373   else if (anim_status_new == GAME_MODE_SCORES)
9374     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9375
9376   global.anim_status_next = anim_status_new;
9377
9378   // directly set screen modes that are entered without fading
9379   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
9380        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9381       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
9382        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9383       (global.anim_status      == GAME_MODE_PSEUDO_NAMESONLY &&
9384        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9385       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAMES &&
9386        global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9387     global.anim_status = global.anim_status_next;
9388 }
9389
9390 void SetGameStatus(int game_status_new)
9391 {
9392   if (game_status_new != game_status)
9393     game_status_last_screen = game_status;
9394
9395   game_status = game_status_new;
9396
9397   SetAnimStatus(game_status_new);
9398 }
9399
9400 void SetFontStatus(int game_status_new)
9401 {
9402   static int last_game_status = -1;
9403
9404   if (game_status_new != -1)
9405   {
9406     // set game status for font use after storing last game status
9407     last_game_status = game_status;
9408     game_status = game_status_new;
9409   }
9410   else
9411   {
9412     // reset game status after font use from last stored game status
9413     game_status = last_game_status;
9414   }
9415 }
9416
9417 void ResetFontStatus(void)
9418 {
9419   SetFontStatus(-1);
9420 }
9421
9422 void SetLevelSetInfo(char *identifier, int level_nr)
9423 {
9424   setString(&levelset.identifier, identifier);
9425
9426   levelset.level_nr = level_nr;
9427 }
9428
9429 boolean CheckIfAllViewportsHaveChanged(void)
9430 {
9431   // if game status has not changed, viewports have not changed either
9432   if (game_status == game_status_last)
9433     return FALSE;
9434
9435   // check if all viewports have changed with current game status
9436
9437   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9438   struct RectWithBorder *vp_door_1    = &viewport.door_1[game_status];
9439   struct RectWithBorder *vp_door_2    = &viewport.door_2[game_status];
9440   int new_real_sx       = vp_playfield->x;
9441   int new_real_sy       = vp_playfield->y;
9442   int new_full_sxsize   = vp_playfield->width;
9443   int new_full_sysize   = vp_playfield->height;
9444   int new_dx            = vp_door_1->x;
9445   int new_dy            = vp_door_1->y;
9446   int new_dxsize        = vp_door_1->width;
9447   int new_dysize        = vp_door_1->height;
9448   int new_vx            = vp_door_2->x;
9449   int new_vy            = vp_door_2->y;
9450   int new_vxsize        = vp_door_2->width;
9451   int new_vysize        = vp_door_2->height;
9452
9453   boolean playfield_viewport_has_changed =
9454     (new_real_sx != REAL_SX ||
9455      new_real_sy != REAL_SY ||
9456      new_full_sxsize != FULL_SXSIZE ||
9457      new_full_sysize != FULL_SYSIZE);
9458
9459   boolean door_1_viewport_has_changed =
9460     (new_dx != DX ||
9461      new_dy != DY ||
9462      new_dxsize != DXSIZE ||
9463      new_dysize != DYSIZE);
9464
9465   boolean door_2_viewport_has_changed =
9466     (new_vx != VX ||
9467      new_vy != VY ||
9468      new_vxsize != VXSIZE ||
9469      new_vysize != VYSIZE ||
9470      game_status_last == GAME_MODE_EDITOR);
9471
9472   return (playfield_viewport_has_changed &&
9473           door_1_viewport_has_changed &&
9474           door_2_viewport_has_changed);
9475 }
9476
9477 boolean CheckFadeAll(void)
9478 {
9479   return (CheckIfGlobalBorderHasChanged() ||
9480           CheckIfAllViewportsHaveChanged());
9481 }
9482
9483 void ChangeViewportPropertiesIfNeeded(void)
9484 {
9485   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9486                                FALSE : setup.small_game_graphics);
9487   int gfx_game_mode = game_status;
9488   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9489                         game_status);
9490   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
9491   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9492   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
9493   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
9494   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
9495   int new_win_xsize     = vp_window->width;
9496   int new_win_ysize     = vp_window->height;
9497   int border_left       = vp_playfield->border_left;
9498   int border_right      = vp_playfield->border_right;
9499   int border_top        = vp_playfield->border_top;
9500   int border_bottom     = vp_playfield->border_bottom;
9501   int new_sx            = vp_playfield->x      + border_left;
9502   int new_sy            = vp_playfield->y      + border_top;
9503   int new_sxsize        = vp_playfield->width  - border_left - border_right;
9504   int new_sysize        = vp_playfield->height - border_top  - border_bottom;
9505   int new_real_sx       = vp_playfield->x;
9506   int new_real_sy       = vp_playfield->y;
9507   int new_full_sxsize   = vp_playfield->width;
9508   int new_full_sysize   = vp_playfield->height;
9509   int new_dx            = vp_door_1->x;
9510   int new_dy            = vp_door_1->y;
9511   int new_dxsize        = vp_door_1->width;
9512   int new_dysize        = vp_door_1->height;
9513   int new_vx            = vp_door_2->x;
9514   int new_vy            = vp_door_2->y;
9515   int new_vxsize        = vp_door_2->width;
9516   int new_vysize        = vp_door_2->height;
9517   int new_ex            = vp_door_3->x;
9518   int new_ey            = vp_door_3->y;
9519   int new_exsize        = vp_door_3->width;
9520   int new_eysize        = vp_door_3->height;
9521   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9522   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9523                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9524   int new_scr_fieldx = new_sxsize / tilesize;
9525   int new_scr_fieldy = new_sysize / tilesize;
9526   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9527   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9528   boolean init_gfx_buffers = FALSE;
9529   boolean init_video_buffer = FALSE;
9530   boolean init_gadgets_and_anims = FALSE;
9531   boolean init_em_graphics = FALSE;
9532
9533   if (new_win_xsize != WIN_XSIZE ||
9534       new_win_ysize != WIN_YSIZE)
9535   {
9536     WIN_XSIZE = new_win_xsize;
9537     WIN_YSIZE = new_win_ysize;
9538
9539     init_video_buffer = TRUE;
9540     init_gfx_buffers = TRUE;
9541     init_gadgets_and_anims = TRUE;
9542
9543     // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9544   }
9545
9546   if (new_scr_fieldx != SCR_FIELDX ||
9547       new_scr_fieldy != SCR_FIELDY)
9548   {
9549     // this always toggles between MAIN and GAME when using small tile size
9550
9551     SCR_FIELDX = new_scr_fieldx;
9552     SCR_FIELDY = new_scr_fieldy;
9553
9554     // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9555   }
9556
9557   if (new_sx != SX ||
9558       new_sy != SY ||
9559       new_dx != DX ||
9560       new_dy != DY ||
9561       new_vx != VX ||
9562       new_vy != VY ||
9563       new_ex != EX ||
9564       new_ey != EY ||
9565       new_sxsize != SXSIZE ||
9566       new_sysize != SYSIZE ||
9567       new_dxsize != DXSIZE ||
9568       new_dysize != DYSIZE ||
9569       new_vxsize != VXSIZE ||
9570       new_vysize != VYSIZE ||
9571       new_exsize != EXSIZE ||
9572       new_eysize != EYSIZE ||
9573       new_real_sx != REAL_SX ||
9574       new_real_sy != REAL_SY ||
9575       new_full_sxsize != FULL_SXSIZE ||
9576       new_full_sysize != FULL_SYSIZE ||
9577       new_tilesize_var != TILESIZE_VAR
9578       )
9579   {
9580     // ------------------------------------------------------------------------
9581     // determine next fading area for changed viewport definitions
9582     // ------------------------------------------------------------------------
9583
9584     // start with current playfield area (default fading area)
9585     FADE_SX = REAL_SX;
9586     FADE_SY = REAL_SY;
9587     FADE_SXSIZE = FULL_SXSIZE;
9588     FADE_SYSIZE = FULL_SYSIZE;
9589
9590     // add new playfield area if position or size has changed
9591     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9592         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9593     {
9594       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9595                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9596     }
9597
9598     // add current and new door 1 area if position or size has changed
9599     if (new_dx != DX || new_dy != DY ||
9600         new_dxsize != DXSIZE || new_dysize != DYSIZE)
9601     {
9602       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9603                      DX, DY, DXSIZE, DYSIZE);
9604       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9605                      new_dx, new_dy, new_dxsize, new_dysize);
9606     }
9607
9608     // add current and new door 2 area if position or size has changed
9609     if (new_vx != VX || new_vy != VY ||
9610         new_vxsize != VXSIZE || new_vysize != VYSIZE)
9611     {
9612       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9613                      VX, VY, VXSIZE, VYSIZE);
9614       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9615                      new_vx, new_vy, new_vxsize, new_vysize);
9616     }
9617
9618     // ------------------------------------------------------------------------
9619     // handle changed tile size
9620     // ------------------------------------------------------------------------
9621
9622     if (new_tilesize_var != TILESIZE_VAR)
9623     {
9624       // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9625
9626       // changing tile size invalidates scroll values of engine snapshots
9627       FreeEngineSnapshotSingle();
9628
9629       // changing tile size requires update of graphic mapping for EM engine
9630       init_em_graphics = TRUE;
9631     }
9632
9633     SX = new_sx;
9634     SY = new_sy;
9635     DX = new_dx;
9636     DY = new_dy;
9637     VX = new_vx;
9638     VY = new_vy;
9639     EX = new_ex;
9640     EY = new_ey;
9641     SXSIZE = new_sxsize;
9642     SYSIZE = new_sysize;
9643     DXSIZE = new_dxsize;
9644     DYSIZE = new_dysize;
9645     VXSIZE = new_vxsize;
9646     VYSIZE = new_vysize;
9647     EXSIZE = new_exsize;
9648     EYSIZE = new_eysize;
9649     REAL_SX = new_real_sx;
9650     REAL_SY = new_real_sy;
9651     FULL_SXSIZE = new_full_sxsize;
9652     FULL_SYSIZE = new_full_sysize;
9653     TILESIZE_VAR = new_tilesize_var;
9654
9655     init_gfx_buffers = TRUE;
9656     init_gadgets_and_anims = TRUE;
9657
9658     // Debug("tools:viewport", "viewports: init_gfx_buffers");
9659     // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9660   }
9661
9662   if (init_gfx_buffers)
9663   {
9664     // Debug("tools:viewport", "init_gfx_buffers");
9665
9666     SCR_FIELDX = new_scr_fieldx_buffers;
9667     SCR_FIELDY = new_scr_fieldy_buffers;
9668
9669     InitGfxBuffers();
9670
9671     SCR_FIELDX = new_scr_fieldx;
9672     SCR_FIELDY = new_scr_fieldy;
9673
9674     SetDrawDeactivationMask(REDRAW_NONE);
9675     SetDrawBackgroundMask(REDRAW_FIELD);
9676   }
9677
9678   if (init_video_buffer)
9679   {
9680     // Debug("tools:viewport", "init_video_buffer");
9681
9682     FreeAllImageTextures();     // needs old renderer to free the textures
9683
9684     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9685     InitImageTextures();
9686   }
9687
9688   if (init_gadgets_and_anims)
9689   {
9690     // Debug("tools:viewport", "init_gadgets_and_anims");
9691
9692     InitGadgets();
9693     InitGlobalAnimations();
9694   }
9695
9696   if (init_em_graphics)
9697   {
9698     InitGraphicInfo_EM();
9699   }
9700 }