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