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