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