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