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