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