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