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