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