fixed bug with playing team mode tapes with changed visible playfield size
[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)
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_last);
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   unsigned int old_door_state;
4608   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4609   int font_nr = FONT_TEXT_2;
4610   char *text_ptr;
4611   int result;
4612   int ty;
4613
4614   if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4615   {
4616     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4617     font_nr = FONT_TEXT_1;
4618   }
4619
4620   if (game_status == GAME_MODE_PLAYING)
4621     BlitScreenToBitmap(backbuffer);
4622
4623   // disable deactivated drawing when quick-loading level tape recording
4624   if (tape.playing && tape.deactivate_display)
4625     TapeDeactivateDisplayOff(TRUE);
4626
4627   SetMouseCursor(CURSOR_DEFAULT);
4628
4629   // pause network game while waiting for request to answer
4630   if (network.enabled &&
4631       game_status == GAME_MODE_PLAYING &&
4632       !game.all_players_gone &&
4633       req_state & REQUEST_WAIT_FOR_INPUT)
4634     SendToServer_PausePlaying();
4635
4636   old_door_state = GetDoorState();
4637
4638   // simulate releasing mouse button over last gadget, if still pressed
4639   if (button_status)
4640     HandleGadgets(-1, -1, 0);
4641
4642   UnmapAllGadgets();
4643
4644   // draw released gadget before proceeding
4645   // BackToFront();
4646
4647   if (old_door_state & DOOR_OPEN_1)
4648   {
4649     CloseDoor(DOOR_CLOSE_1);
4650
4651     // save old door content
4652     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4653                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4654   }
4655
4656   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4657   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4658
4659   // clear door drawing field
4660   DrawBackground(DX, DY, DXSIZE, DYSIZE);
4661
4662   // force DOOR font inside door area
4663   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4664
4665   // write text for request
4666   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4667   {
4668     char text_line[max_request_line_len + 1];
4669     int tx, tl, tc = 0;
4670
4671     if (!*text_ptr)
4672       break;
4673
4674     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4675     {
4676       tc = *(text_ptr + tx);
4677       // if (!tc || tc == ' ')
4678       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4679         break;
4680     }
4681
4682     if ((tc == '?' || tc == '!') && tl == 0)
4683       tl = 1;
4684
4685     if (!tl)
4686     { 
4687       text_ptr++; 
4688       ty--; 
4689       continue; 
4690     }
4691
4692     strncpy(text_line, text_ptr, tl);
4693     text_line[tl] = 0;
4694
4695     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4696              DY + 8 + ty * (getFontHeight(font_nr) + 2),
4697              text_line, font_nr);
4698
4699     text_ptr += tl + (tc == ' ' ? 1 : 0);
4700     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4701   }
4702
4703   ResetFontStatus();
4704
4705   if (req_state & REQ_ASK)
4706   {
4707     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4708     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4709     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4710     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4711   }
4712   else if (req_state & REQ_CONFIRM)
4713   {
4714     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4715     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4716   }
4717   else if (req_state & REQ_PLAYER)
4718   {
4719     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4720     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4721     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4722     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4723   }
4724
4725   // copy request gadgets to door backbuffer
4726   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4727
4728   OpenDoor(DOOR_OPEN_1);
4729
4730   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4731   {
4732     if (game_status == GAME_MODE_PLAYING)
4733     {
4734       SetPanelBackground();
4735       SetDrawBackgroundMask(REDRAW_DOOR_1);
4736     }
4737     else
4738     {
4739       SetDrawBackgroundMask(REDRAW_FIELD);
4740     }
4741
4742     return FALSE;
4743   }
4744
4745   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4746
4747   // ---------- handle request buttons ----------
4748   result = RequestHandleEvents(req_state);
4749
4750   UnmapToolButtons();
4751
4752   if (!(req_state & REQ_STAY_OPEN))
4753   {
4754     CloseDoor(DOOR_CLOSE_1);
4755
4756     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4757         (req_state & REQ_REOPEN))
4758       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4759   }
4760
4761   RemapAllGadgets();
4762
4763   if (game_status == GAME_MODE_PLAYING)
4764   {
4765     SetPanelBackground();
4766     SetDrawBackgroundMask(REDRAW_DOOR_1);
4767   }
4768   else
4769   {
4770     SetDrawBackgroundMask(REDRAW_FIELD);
4771   }
4772
4773   // continue network game after request
4774   if (network.enabled &&
4775       game_status == GAME_MODE_PLAYING &&
4776       !game.all_players_gone &&
4777       req_state & REQUEST_WAIT_FOR_INPUT)
4778     SendToServer_ContinuePlaying();
4779
4780   // restore deactivated drawing when quick-loading level tape recording
4781   if (tape.playing && tape.deactivate_display)
4782     TapeDeactivateDisplayOn();
4783
4784   return result;
4785 }
4786
4787 static boolean RequestEnvelope(char *text, unsigned int req_state)
4788 {
4789   int result;
4790
4791   if (game_status == GAME_MODE_PLAYING)
4792     BlitScreenToBitmap(backbuffer);
4793
4794   // disable deactivated drawing when quick-loading level tape recording
4795   if (tape.playing && tape.deactivate_display)
4796     TapeDeactivateDisplayOff(TRUE);
4797
4798   SetMouseCursor(CURSOR_DEFAULT);
4799
4800   // pause network game while waiting for request to answer
4801   if (network.enabled &&
4802       game_status == GAME_MODE_PLAYING &&
4803       !game.all_players_gone &&
4804       req_state & REQUEST_WAIT_FOR_INPUT)
4805     SendToServer_PausePlaying();
4806
4807   // simulate releasing mouse button over last gadget, if still pressed
4808   if (button_status)
4809     HandleGadgets(-1, -1, 0);
4810
4811   UnmapAllGadgets();
4812
4813   // (replace with setting corresponding request background)
4814   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4815   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4816
4817   // clear door drawing field
4818   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4819
4820   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4821
4822   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4823   {
4824     if (game_status == GAME_MODE_PLAYING)
4825     {
4826       SetPanelBackground();
4827       SetDrawBackgroundMask(REDRAW_DOOR_1);
4828     }
4829     else
4830     {
4831       SetDrawBackgroundMask(REDRAW_FIELD);
4832     }
4833
4834     return FALSE;
4835   }
4836
4837   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4838
4839   // ---------- handle request buttons ----------
4840   result = RequestHandleEvents(req_state);
4841
4842   UnmapToolButtons();
4843
4844   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4845
4846   RemapAllGadgets();
4847
4848   if (game_status == GAME_MODE_PLAYING)
4849   {
4850     SetPanelBackground();
4851     SetDrawBackgroundMask(REDRAW_DOOR_1);
4852   }
4853   else
4854   {
4855     SetDrawBackgroundMask(REDRAW_FIELD);
4856   }
4857
4858   // continue network game after request
4859   if (network.enabled &&
4860       game_status == GAME_MODE_PLAYING &&
4861       !game.all_players_gone &&
4862       req_state & REQUEST_WAIT_FOR_INPUT)
4863     SendToServer_ContinuePlaying();
4864
4865   // restore deactivated drawing when quick-loading level tape recording
4866   if (tape.playing && tape.deactivate_display)
4867     TapeDeactivateDisplayOn();
4868
4869   return result;
4870 }
4871
4872 boolean Request(char *text, unsigned int req_state)
4873 {
4874   boolean overlay_enabled = GetOverlayEnabled();
4875   boolean result;
4876
4877   game.request_active_or_moving = TRUE;
4878
4879   SetOverlayEnabled(FALSE);
4880
4881   if (global.use_envelope_request)
4882     result = RequestEnvelope(text, req_state);
4883   else
4884     result = RequestDoor(text, req_state);
4885
4886   SetOverlayEnabled(overlay_enabled);
4887
4888   game.request_active_or_moving = FALSE;
4889
4890   return result;
4891 }
4892
4893 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4894 {
4895   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4896   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4897   int compare_result;
4898
4899   if (dpo1->sort_priority != dpo2->sort_priority)
4900     compare_result = dpo1->sort_priority - dpo2->sort_priority;
4901   else
4902     compare_result = dpo1->nr - dpo2->nr;
4903
4904   return compare_result;
4905 }
4906
4907 void InitGraphicCompatibilityInfo_Doors(void)
4908 {
4909   struct
4910   {
4911     int door_token;
4912     int part_1, part_8;
4913     struct DoorInfo *door;
4914   }
4915   doors[] =
4916   {
4917     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
4918     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
4919
4920     { -1,       -1,                     -1,                     NULL    }
4921   };
4922   struct Rect door_rect_list[] =
4923   {
4924     { DX, DY, DXSIZE, DYSIZE },
4925     { VX, VY, VXSIZE, VYSIZE }
4926   };
4927   int i, j;
4928
4929   for (i = 0; doors[i].door_token != -1; i++)
4930   {
4931     int door_token = doors[i].door_token;
4932     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4933     int part_1 = doors[i].part_1;
4934     int part_8 = doors[i].part_8;
4935     int part_2 = part_1 + 1;
4936     int part_3 = part_1 + 2;
4937     struct DoorInfo *door = doors[i].door;
4938     struct Rect *door_rect = &door_rect_list[door_index];
4939     boolean door_gfx_redefined = FALSE;
4940
4941     // check if any door part graphic definitions have been redefined
4942
4943     for (j = 0; door_part_controls[j].door_token != -1; j++)
4944     {
4945       struct DoorPartControlInfo *dpc = &door_part_controls[j];
4946       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4947
4948       if (dpc->door_token == door_token && fi->redefined)
4949         door_gfx_redefined = TRUE;
4950     }
4951
4952     // check for old-style door graphic/animation modifications
4953
4954     if (!door_gfx_redefined)
4955     {
4956       if (door->anim_mode & ANIM_STATIC_PANEL)
4957       {
4958         door->panel.step_xoffset = 0;
4959         door->panel.step_yoffset = 0;
4960       }
4961
4962       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4963       {
4964         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4965         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4966         int num_door_steps, num_panel_steps;
4967
4968         // remove door part graphics other than the two default wings
4969
4970         for (j = 0; door_part_controls[j].door_token != -1; j++)
4971         {
4972           struct DoorPartControlInfo *dpc = &door_part_controls[j];
4973           struct GraphicInfo *g = &graphic_info[dpc->graphic];
4974
4975           if (dpc->graphic >= part_3 &&
4976               dpc->graphic <= part_8)
4977             g->bitmap = NULL;
4978         }
4979
4980         // set graphics and screen positions of the default wings
4981
4982         g_part_1->width  = door_rect->width;
4983         g_part_1->height = door_rect->height;
4984         g_part_2->width  = door_rect->width;
4985         g_part_2->height = door_rect->height;
4986         g_part_2->src_x = door_rect->width;
4987         g_part_2->src_y = g_part_1->src_y;
4988
4989         door->part_2.x = door->part_1.x;
4990         door->part_2.y = door->part_1.y;
4991
4992         if (door->width != -1)
4993         {
4994           g_part_1->width = door->width;
4995           g_part_2->width = door->width;
4996
4997           // special treatment for graphics and screen position of right wing
4998           g_part_2->src_x += door_rect->width - door->width;
4999           door->part_2.x  += door_rect->width - door->width;
5000         }
5001
5002         if (door->height != -1)
5003         {
5004           g_part_1->height = door->height;
5005           g_part_2->height = door->height;
5006
5007           // special treatment for graphics and screen position of bottom wing
5008           g_part_2->src_y += door_rect->height - door->height;
5009           door->part_2.y  += door_rect->height - door->height;
5010         }
5011
5012         // set animation delays for the default wings and panels
5013
5014         door->part_1.step_delay = door->step_delay;
5015         door->part_2.step_delay = door->step_delay;
5016         door->panel.step_delay  = door->step_delay;
5017
5018         // set animation draw order for the default wings
5019
5020         door->part_1.sort_priority = 2; // draw left wing over ...
5021         door->part_2.sort_priority = 1; //          ... right wing
5022
5023         // set animation draw offset for the default wings
5024
5025         if (door->anim_mode & ANIM_HORIZONTAL)
5026         {
5027           door->part_1.step_xoffset = door->step_offset;
5028           door->part_1.step_yoffset = 0;
5029           door->part_2.step_xoffset = door->step_offset * -1;
5030           door->part_2.step_yoffset = 0;
5031
5032           num_door_steps = g_part_1->width / door->step_offset;
5033         }
5034         else    // ANIM_VERTICAL
5035         {
5036           door->part_1.step_xoffset = 0;
5037           door->part_1.step_yoffset = door->step_offset;
5038           door->part_2.step_xoffset = 0;
5039           door->part_2.step_yoffset = door->step_offset * -1;
5040
5041           num_door_steps = g_part_1->height / door->step_offset;
5042         }
5043
5044         // set animation draw offset for the default panels
5045
5046         if (door->step_offset > 1)
5047         {
5048           num_panel_steps = 2 * door_rect->height / door->step_offset;
5049           door->panel.start_step = num_panel_steps - num_door_steps;
5050           door->panel.start_step_closing = door->panel.start_step;
5051         }
5052         else
5053         {
5054           num_panel_steps = door_rect->height / door->step_offset;
5055           door->panel.start_step = num_panel_steps - num_door_steps / 2;
5056           door->panel.start_step_closing = door->panel.start_step;
5057           door->panel.step_delay *= 2;
5058         }
5059       }
5060     }
5061   }
5062 }
5063
5064 void InitDoors(void)
5065 {
5066   int i;
5067
5068   for (i = 0; door_part_controls[i].door_token != -1; i++)
5069   {
5070     struct DoorPartControlInfo *dpc = &door_part_controls[i];
5071     struct DoorPartOrderInfo *dpo = &door_part_order[i];
5072
5073     // initialize "start_step_opening" and "start_step_closing", if needed
5074     if (dpc->pos->start_step_opening == 0 &&
5075         dpc->pos->start_step_closing == 0)
5076     {
5077       // dpc->pos->start_step_opening = dpc->pos->start_step;
5078       dpc->pos->start_step_closing = dpc->pos->start_step;
5079     }
5080
5081     // fill structure for door part draw order (sorted below)
5082     dpo->nr = i;
5083     dpo->sort_priority = dpc->pos->sort_priority;
5084   }
5085
5086   // sort door part controls according to sort_priority and graphic number
5087   qsort(door_part_order, MAX_DOOR_PARTS,
5088         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5089 }
5090
5091 unsigned int OpenDoor(unsigned int door_state)
5092 {
5093   if (door_state & DOOR_COPY_BACK)
5094   {
5095     if (door_state & DOOR_OPEN_1)
5096       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5097                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5098
5099     if (door_state & DOOR_OPEN_2)
5100       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5101                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5102
5103     door_state &= ~DOOR_COPY_BACK;
5104   }
5105
5106   return MoveDoor(door_state);
5107 }
5108
5109 unsigned int CloseDoor(unsigned int door_state)
5110 {
5111   unsigned int old_door_state = GetDoorState();
5112
5113   if (!(door_state & DOOR_NO_COPY_BACK))
5114   {
5115     if (old_door_state & DOOR_OPEN_1)
5116       BlitBitmap(backbuffer, bitmap_db_door_1,
5117                  DX, DY, DXSIZE, DYSIZE, 0, 0);
5118
5119     if (old_door_state & DOOR_OPEN_2)
5120       BlitBitmap(backbuffer, bitmap_db_door_2,
5121                  VX, VY, VXSIZE, VYSIZE, 0, 0);
5122
5123     door_state &= ~DOOR_NO_COPY_BACK;
5124   }
5125
5126   return MoveDoor(door_state);
5127 }
5128
5129 unsigned int GetDoorState(void)
5130 {
5131   return MoveDoor(DOOR_GET_STATE);
5132 }
5133
5134 unsigned int SetDoorState(unsigned int door_state)
5135 {
5136   return MoveDoor(door_state | DOOR_SET_STATE);
5137 }
5138
5139 static int euclid(int a, int b)
5140 {
5141   return (b ? euclid(b, a % b) : a);
5142 }
5143
5144 unsigned int MoveDoor(unsigned int door_state)
5145 {
5146   struct Rect door_rect_list[] =
5147   {
5148     { DX, DY, DXSIZE, DYSIZE },
5149     { VX, VY, VXSIZE, VYSIZE }
5150   };
5151   static int door1 = DOOR_CLOSE_1;
5152   static int door2 = DOOR_CLOSE_2;
5153   unsigned int door_delay = 0;
5154   unsigned int door_delay_value;
5155   int i;
5156
5157   if (door_state == DOOR_GET_STATE)
5158     return (door1 | door2);
5159
5160   if (door_state & DOOR_SET_STATE)
5161   {
5162     if (door_state & DOOR_ACTION_1)
5163       door1 = door_state & DOOR_ACTION_1;
5164     if (door_state & DOOR_ACTION_2)
5165       door2 = door_state & DOOR_ACTION_2;
5166
5167     return (door1 | door2);
5168   }
5169
5170   if (!(door_state & DOOR_FORCE_REDRAW))
5171   {
5172     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5173       door_state &= ~DOOR_OPEN_1;
5174     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5175       door_state &= ~DOOR_CLOSE_1;
5176     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5177       door_state &= ~DOOR_OPEN_2;
5178     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5179       door_state &= ~DOOR_CLOSE_2;
5180   }
5181
5182   if (global.autoplay_leveldir)
5183   {
5184     door_state |= DOOR_NO_DELAY;
5185     door_state &= ~DOOR_CLOSE_ALL;
5186   }
5187
5188   if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5189     door_state |= DOOR_NO_DELAY;
5190
5191   if (door_state & DOOR_ACTION)
5192   {
5193     boolean door_panel_drawn[NUM_DOORS];
5194     boolean panel_has_doors[NUM_DOORS];
5195     boolean door_part_skip[MAX_DOOR_PARTS];
5196     boolean door_part_done[MAX_DOOR_PARTS];
5197     boolean door_part_done_all;
5198     int num_steps[MAX_DOOR_PARTS];
5199     int max_move_delay = 0;     // delay for complete animations of all doors
5200     int max_step_delay = 0;     // delay (ms) between two animation frames
5201     int num_move_steps = 0;     // number of animation steps for all doors
5202     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
5203     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
5204     int current_move_delay = 0;
5205     int start = 0;
5206     int k;
5207
5208     for (i = 0; i < NUM_DOORS; i++)
5209       panel_has_doors[i] = FALSE;
5210
5211     for (i = 0; i < MAX_DOOR_PARTS; i++)
5212     {
5213       struct DoorPartControlInfo *dpc = &door_part_controls[i];
5214       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5215       int door_token = dpc->door_token;
5216
5217       door_part_done[i] = FALSE;
5218       door_part_skip[i] = (!(door_state & door_token) ||
5219                            !g->bitmap);
5220     }
5221
5222     for (i = 0; i < MAX_DOOR_PARTS; i++)
5223     {
5224       int nr = door_part_order[i].nr;
5225       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5226       struct DoorPartPosInfo *pos = dpc->pos;
5227       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5228       int door_token = dpc->door_token;
5229       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5230       boolean is_panel = DOOR_PART_IS_PANEL(nr);
5231       int step_xoffset = ABS(pos->step_xoffset);
5232       int step_yoffset = ABS(pos->step_yoffset);
5233       int step_delay = pos->step_delay;
5234       int current_door_state = door_state & door_token;
5235       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5236       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5237       boolean part_opening = (is_panel ? door_closing : door_opening);
5238       int start_step = (part_opening ? pos->start_step_opening :
5239                         pos->start_step_closing);
5240       float move_xsize = (step_xoffset ? g->width  : 0);
5241       float move_ysize = (step_yoffset ? g->height : 0);
5242       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5243       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5244       int move_steps = (move_xsteps && move_ysteps ?
5245                         MIN(move_xsteps, move_ysteps) :
5246                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
5247       int move_delay = move_steps * step_delay;
5248
5249       if (door_part_skip[nr])
5250         continue;
5251
5252       max_move_delay = MAX(max_move_delay, move_delay);
5253       max_step_delay = (max_step_delay == 0 ? step_delay :
5254                         euclid(max_step_delay, step_delay));
5255       num_steps[nr] = move_steps;
5256
5257       if (!is_panel)
5258       {
5259         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5260
5261         panel_has_doors[door_index] = TRUE;
5262       }
5263     }
5264
5265     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
5266
5267     num_move_steps = max_move_delay / max_step_delay;
5268     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5269
5270     door_delay_value = max_step_delay;
5271
5272     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5273     {
5274       start = num_move_steps - 1;
5275     }
5276     else
5277     {
5278       // opening door sound has priority over simultaneously closing door
5279       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5280       {
5281         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5282
5283         if (door_state & DOOR_OPEN_1)
5284           PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5285         if (door_state & DOOR_OPEN_2)
5286           PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5287       }
5288       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5289       {
5290         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5291
5292         if (door_state & DOOR_CLOSE_1)
5293           PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5294         if (door_state & DOOR_CLOSE_2)
5295           PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5296       }
5297     }
5298
5299     for (k = start; k < num_move_steps; k++)
5300     {
5301       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
5302
5303       door_part_done_all = TRUE;
5304
5305       for (i = 0; i < NUM_DOORS; i++)
5306         door_panel_drawn[i] = FALSE;
5307
5308       for (i = 0; i < MAX_DOOR_PARTS; i++)
5309       {
5310         int nr = door_part_order[i].nr;
5311         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5312         struct DoorPartPosInfo *pos = dpc->pos;
5313         struct GraphicInfo *g = &graphic_info[dpc->graphic];
5314         int door_token = dpc->door_token;
5315         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5316         boolean is_panel = DOOR_PART_IS_PANEL(nr);
5317         boolean is_panel_and_door_has_closed = FALSE;
5318         struct Rect *door_rect = &door_rect_list[door_index];
5319         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5320                                   bitmap_db_door_2);
5321         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5322         int current_door_state = door_state & door_token;
5323         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5324         boolean door_closing = !door_opening;
5325         boolean part_opening = (is_panel ? door_closing : door_opening);
5326         boolean part_closing = !part_opening;
5327         int start_step = (part_opening ? pos->start_step_opening :
5328                           pos->start_step_closing);
5329         int step_delay = pos->step_delay;
5330         int step_factor = step_delay / max_step_delay;
5331         int k1 = (step_factor ? k / step_factor + 1 : k);
5332         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5333         int kk = MAX(0, k2);
5334         int g_src_x = 0;
5335         int g_src_y = 0;
5336         int src_x, src_y, src_xx, src_yy;
5337         int dst_x, dst_y, dst_xx, dst_yy;
5338         int width, height;
5339
5340         if (door_part_skip[nr])
5341           continue;
5342
5343         if (!(door_state & door_token))
5344           continue;
5345
5346         if (!g->bitmap)
5347           continue;
5348
5349         if (!is_panel)
5350         {
5351           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5352           int kk_door = MAX(0, k2_door);
5353           int sync_frame = kk_door * door_delay_value;
5354           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5355
5356           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5357                                 &g_src_x, &g_src_y);
5358         }
5359
5360         // draw door panel
5361
5362         if (!door_panel_drawn[door_index])
5363         {
5364           ClearRectangle(drawto, door_rect->x, door_rect->y,
5365                          door_rect->width, door_rect->height);
5366
5367           door_panel_drawn[door_index] = TRUE;
5368         }
5369
5370         // draw opening or closing door parts
5371
5372         if (pos->step_xoffset < 0)      // door part on right side
5373         {
5374           src_xx = 0;
5375           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5376           width = g->width;
5377
5378           if (dst_xx + width > door_rect->width)
5379             width = door_rect->width - dst_xx;
5380         }
5381         else                            // door part on left side
5382         {
5383           src_xx = 0;
5384           dst_xx = pos->x - kk * pos->step_xoffset;
5385
5386           if (dst_xx < 0)
5387           {
5388             src_xx = ABS(dst_xx);
5389             dst_xx = 0;
5390           }
5391
5392           width = g->width - src_xx;
5393
5394           if (width > door_rect->width)
5395             width = door_rect->width;
5396
5397           // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5398         }
5399
5400         if (pos->step_yoffset < 0)      // door part on bottom side
5401         {
5402           src_yy = 0;
5403           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5404           height = g->height;
5405
5406           if (dst_yy + height > door_rect->height)
5407             height = door_rect->height - dst_yy;
5408         }
5409         else                            // door part on top side
5410         {
5411           src_yy = 0;
5412           dst_yy = pos->y - kk * pos->step_yoffset;
5413
5414           if (dst_yy < 0)
5415           {
5416             src_yy = ABS(dst_yy);
5417             dst_yy = 0;
5418           }
5419
5420           height = g->height - src_yy;
5421         }
5422
5423         src_x = g_src_x + src_xx;
5424         src_y = g_src_y + src_yy;
5425
5426         dst_x = door_rect->x + dst_xx;
5427         dst_y = door_rect->y + dst_yy;
5428
5429         is_panel_and_door_has_closed =
5430           (is_panel &&
5431            door_closing &&
5432            panel_has_doors[door_index] &&
5433            k >= num_move_steps_doors_only - 1);
5434
5435         if (width  >= 0 && width  <= g->width &&
5436             height >= 0 && height <= g->height &&
5437             !is_panel_and_door_has_closed)
5438         {
5439           if (is_panel || !pos->draw_masked)
5440             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5441                        dst_x, dst_y);
5442           else
5443             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5444                              dst_x, dst_y);
5445         }
5446
5447         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5448
5449         if ((part_opening && (width < 0         || height < 0)) ||
5450             (part_closing && (width >= g->width && height >= g->height)))
5451           door_part_done[nr] = TRUE;
5452
5453         // continue door part animations, but not panel after door has closed
5454         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5455           door_part_done_all = FALSE;
5456       }
5457
5458       if (!(door_state & DOOR_NO_DELAY))
5459       {
5460         BackToFront();
5461
5462         SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5463
5464         current_move_delay += max_step_delay;
5465
5466         // prevent OS (Windows) from complaining about program not responding
5467         CheckQuitEvent();
5468       }
5469
5470       if (door_part_done_all)
5471         break;
5472     }
5473
5474     if (!(door_state & DOOR_NO_DELAY))
5475     {
5476       // wait for specified door action post delay
5477       if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5478         door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5479       else if (door_state & DOOR_ACTION_1)
5480         door_delay_value = door_1.post_delay;
5481       else if (door_state & DOOR_ACTION_2)
5482         door_delay_value = door_2.post_delay;
5483
5484       while (!DelayReached(&door_delay, door_delay_value))
5485         BackToFront();
5486     }
5487   }
5488
5489   if (door_state & DOOR_ACTION_1)
5490     door1 = door_state & DOOR_ACTION_1;
5491   if (door_state & DOOR_ACTION_2)
5492     door2 = door_state & DOOR_ACTION_2;
5493
5494   // draw masked border over door area
5495   DrawMaskedBorder(REDRAW_DOOR_1);
5496   DrawMaskedBorder(REDRAW_DOOR_2);
5497
5498   ClearAutoRepeatKeyEvents();
5499
5500   return (door1 | door2);
5501 }
5502
5503 static boolean useSpecialEditorDoor(void)
5504 {
5505   int graphic = IMG_GLOBAL_BORDER_EDITOR;
5506   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5507
5508   // do not draw special editor door if editor border defined or redefined
5509   if (graphic_info[graphic].bitmap != NULL || redefined)
5510     return FALSE;
5511
5512   // do not draw special editor door if global border defined to be empty
5513   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5514     return FALSE;
5515
5516   // do not draw special editor door if viewport definitions do not match
5517   if (EX != VX ||
5518       EY >= VY ||
5519       EXSIZE != VXSIZE ||
5520       EY + EYSIZE != VY + VYSIZE)
5521     return FALSE;
5522
5523   return TRUE;
5524 }
5525
5526 void DrawSpecialEditorDoor(void)
5527 {
5528   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5529   int top_border_width = gfx1->width;
5530   int top_border_height = gfx1->height;
5531   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5532   int ex = EX - outer_border;
5533   int ey = EY - outer_border;
5534   int vy = VY - outer_border;
5535   int exsize = EXSIZE + 2 * outer_border;
5536
5537   if (!useSpecialEditorDoor())
5538     return;
5539
5540   // draw bigger level editor toolbox window
5541   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5542              top_border_width, top_border_height, ex, ey - top_border_height);
5543   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5544              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5545
5546   redraw_mask |= REDRAW_ALL;
5547 }
5548
5549 void UndrawSpecialEditorDoor(void)
5550 {
5551   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5552   int top_border_width = gfx1->width;
5553   int top_border_height = gfx1->height;
5554   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5555   int ex = EX - outer_border;
5556   int ey = EY - outer_border;
5557   int ey_top = ey - top_border_height;
5558   int exsize = EXSIZE + 2 * outer_border;
5559   int eysize = EYSIZE + 2 * outer_border;
5560
5561   if (!useSpecialEditorDoor())
5562     return;
5563
5564   // draw normal tape recorder window
5565   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5566   {
5567     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5568                ex, ey_top, top_border_width, top_border_height,
5569                ex, ey_top);
5570     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5571                ex, ey, exsize, eysize, ex, ey);
5572   }
5573   else
5574   {
5575     // if screen background is set to "[NONE]", clear editor toolbox window
5576     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5577     ClearRectangle(drawto, ex, ey, exsize, eysize);
5578   }
5579
5580   redraw_mask |= REDRAW_ALL;
5581 }
5582
5583
5584 // ---------- new tool button stuff -------------------------------------------
5585
5586 static struct
5587 {
5588   int graphic;
5589   struct TextPosInfo *pos;
5590   int gadget_id;
5591   boolean is_touch_button;
5592   char *infotext;
5593 } toolbutton_info[NUM_TOOL_BUTTONS] =
5594 {
5595   {
5596     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
5597     TOOL_CTRL_ID_YES, FALSE,            "yes"
5598   },
5599   {
5600     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5601     TOOL_CTRL_ID_NO, FALSE,             "no"
5602   },
5603   {
5604     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5605     TOOL_CTRL_ID_CONFIRM, FALSE,        "confirm"
5606   },
5607   {
5608     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5609     TOOL_CTRL_ID_PLAYER_1, FALSE,       "player 1"
5610   },
5611   {
5612     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5613     TOOL_CTRL_ID_PLAYER_2, FALSE,       "player 2"
5614   },
5615   {
5616     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5617     TOOL_CTRL_ID_PLAYER_3, FALSE,       "player 3"
5618   },
5619   {
5620     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5621     TOOL_CTRL_ID_PLAYER_4, FALSE,       "player 4"
5622   },
5623   {
5624     IMG_GFX_REQUEST_BUTTON_TOUCH_YES,   &request.button.touch_yes,
5625     TOOL_CTRL_ID_TOUCH_YES, TRUE,       "yes"
5626   },
5627   {
5628     IMG_GFX_REQUEST_BUTTON_TOUCH_NO,    &request.button.touch_no,
5629     TOOL_CTRL_ID_TOUCH_NO, TRUE,        "no"
5630   },
5631   {
5632     IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5633     TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE,   "confirm"
5634   }
5635 };
5636
5637 void CreateToolButtons(void)
5638 {
5639   int i;
5640
5641   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5642   {
5643     int graphic = toolbutton_info[i].graphic;
5644     struct GraphicInfo *gfx = &graphic_info[graphic];
5645     struct TextPosInfo *pos = toolbutton_info[i].pos;
5646     struct GadgetInfo *gi;
5647     Bitmap *deco_bitmap = None;
5648     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5649     unsigned int event_mask = GD_EVENT_RELEASED;
5650     boolean is_touch_button = toolbutton_info[i].is_touch_button;
5651     int base_x = (is_touch_button ? 0 : DX);
5652     int base_y = (is_touch_button ? 0 : DY);
5653     int gd_x = gfx->src_x;
5654     int gd_y = gfx->src_y;
5655     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5656     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5657     int x = pos->x;
5658     int y = pos->y;
5659     int id = i;
5660
5661     if (global.use_envelope_request && !is_touch_button)
5662     {
5663       setRequestPosition(&base_x, &base_y, TRUE);
5664
5665       // check if request buttons are outside of envelope and fix, if needed
5666       if (x < 0 || x + gfx->width  > request.width ||
5667           y < 0 || y + gfx->height > request.height)
5668       {
5669         if (id == TOOL_CTRL_ID_YES)
5670         {
5671           x = 0;
5672           y = request.height - 2 * request.border_size - gfx->height;
5673         }
5674         else if (id == TOOL_CTRL_ID_NO)
5675         {
5676           x = request.width  - 2 * request.border_size - gfx->width;
5677           y = request.height - 2 * request.border_size - gfx->height;
5678         }
5679         else if (id == TOOL_CTRL_ID_CONFIRM)
5680         {
5681           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5682           y = request.height - 2 * request.border_size - gfx->height;
5683         }
5684         else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5685         {
5686           int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5687
5688           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5689           y = request.height - 2 * request.border_size - gfx->height * 2;
5690
5691           x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5692           y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5693         }
5694       }
5695     }
5696
5697     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5698     {
5699       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5700
5701       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5702                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5703       deco_xpos = (gfx->width  - pos->size) / 2;
5704       deco_ypos = (gfx->height - pos->size) / 2;
5705     }
5706
5707     gi = CreateGadget(GDI_CUSTOM_ID, id,
5708                       GDI_IMAGE_ID, graphic,
5709                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5710                       GDI_X, base_x + x,
5711                       GDI_Y, base_y + y,
5712                       GDI_WIDTH, gfx->width,
5713                       GDI_HEIGHT, gfx->height,
5714                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5715                       GDI_STATE, GD_BUTTON_UNPRESSED,
5716                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5717                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5718                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5719                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5720                       GDI_DECORATION_SIZE, pos->size, pos->size,
5721                       GDI_DECORATION_SHIFTING, 1, 1,
5722                       GDI_DIRECT_DRAW, FALSE,
5723                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5724                       GDI_EVENT_MASK, event_mask,
5725                       GDI_CALLBACK_ACTION, HandleToolButtons,
5726                       GDI_END);
5727
5728     if (gi == NULL)
5729       Fail("cannot create gadget");
5730
5731     tool_gadget[id] = gi;
5732   }
5733 }
5734
5735 void FreeToolButtons(void)
5736 {
5737   int i;
5738
5739   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5740     FreeGadget(tool_gadget[i]);
5741 }
5742
5743 static void UnmapToolButtons(void)
5744 {
5745   int i;
5746
5747   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5748     UnmapGadget(tool_gadget[i]);
5749 }
5750
5751 static void HandleToolButtons(struct GadgetInfo *gi)
5752 {
5753   request_gadget_id = gi->custom_id;
5754 }
5755
5756 static struct Mapping_EM_to_RND_object
5757 {
5758   int element_em;
5759   boolean is_rnd_to_em_mapping;         // unique mapping EM <-> RND
5760   boolean is_backside;                  // backside of moving element
5761
5762   int element_rnd;
5763   int action;
5764   int direction;
5765 }
5766 em_object_mapping_list[GAME_TILE_MAX + 1] =
5767 {
5768   {
5769     Zborder,                            FALSE,  FALSE,
5770     EL_EMPTY,                           -1, -1
5771   },
5772   {
5773     Zplayer,                            FALSE,  FALSE,
5774     EL_EMPTY,                           -1, -1
5775   },
5776
5777   {
5778     Zbug,                               FALSE,  FALSE,
5779     EL_EMPTY,                           -1, -1
5780   },
5781   {
5782     Ztank,                              FALSE,  FALSE,
5783     EL_EMPTY,                           -1, -1
5784   },
5785   {
5786     Zeater,                             FALSE,  FALSE,
5787     EL_EMPTY,                           -1, -1
5788   },
5789   {
5790     Zdynamite,                          FALSE,  FALSE,
5791     EL_EMPTY,                           -1, -1
5792   },
5793   {
5794     Zboom,                              FALSE,  FALSE,
5795     EL_EMPTY,                           -1, -1
5796   },
5797
5798   {
5799     Xchain,                             FALSE,  FALSE,
5800     EL_DEFAULT,                         ACTION_EXPLODING, -1
5801   },
5802   {
5803     Xboom_bug,                          FALSE,  FALSE,
5804     EL_BUG,                             ACTION_EXPLODING, -1
5805   },
5806   {
5807     Xboom_tank,                         FALSE,  FALSE,
5808     EL_SPACESHIP,                       ACTION_EXPLODING, -1
5809   },
5810   {
5811     Xboom_android,                      FALSE,  FALSE,
5812     EL_EMC_ANDROID,                     ACTION_OTHER, -1
5813   },
5814   {
5815     Xboom_1,                            FALSE,  FALSE,
5816     EL_DEFAULT,                         ACTION_EXPLODING, -1
5817   },
5818   {
5819     Xboom_2,                            FALSE,  FALSE,
5820     EL_DEFAULT,                         ACTION_EXPLODING, -1
5821   },
5822
5823   {
5824     Xblank,                             TRUE,   FALSE,
5825     EL_EMPTY,                           -1, -1
5826   },
5827
5828   {
5829     Xsplash_e,                          FALSE,  FALSE,
5830     EL_ACID_SPLASH_RIGHT,               -1, -1
5831   },
5832   {
5833     Xsplash_w,                          FALSE,  FALSE,
5834     EL_ACID_SPLASH_LEFT,                -1, -1
5835   },
5836
5837   {
5838     Xplant,                             TRUE,   FALSE,
5839     EL_EMC_PLANT,                       -1, -1
5840   },
5841   {
5842     Yplant,                             FALSE,  FALSE,
5843     EL_EMC_PLANT,                       -1, -1
5844   },
5845
5846   {
5847     Xacid_1,                            TRUE,   FALSE,
5848     EL_ACID,                            -1, -1
5849   },
5850   {
5851     Xacid_2,                            FALSE,  FALSE,
5852     EL_ACID,                            -1, -1
5853   },
5854   {
5855     Xacid_3,                            FALSE,  FALSE,
5856     EL_ACID,                            -1, -1
5857   },
5858   {
5859     Xacid_4,                            FALSE,  FALSE,
5860     EL_ACID,                            -1, -1
5861   },
5862   {
5863     Xacid_5,                            FALSE,  FALSE,
5864     EL_ACID,                            -1, -1
5865   },
5866   {
5867     Xacid_6,                            FALSE,  FALSE,
5868     EL_ACID,                            -1, -1
5869   },
5870   {
5871     Xacid_7,                            FALSE,  FALSE,
5872     EL_ACID,                            -1, -1
5873   },
5874   {
5875     Xacid_8,                            FALSE,  FALSE,
5876     EL_ACID,                            -1, -1
5877   },
5878
5879   {
5880     Xfake_acid_1,                       TRUE,   FALSE,
5881     EL_EMC_FAKE_ACID,                   -1, -1
5882   },
5883   {
5884     Xfake_acid_2,                       FALSE,  FALSE,
5885     EL_EMC_FAKE_ACID,                   -1, -1
5886   },
5887   {
5888     Xfake_acid_3,                       FALSE,  FALSE,
5889     EL_EMC_FAKE_ACID,                   -1, -1
5890   },
5891   {
5892     Xfake_acid_4,                       FALSE,  FALSE,
5893     EL_EMC_FAKE_ACID,                   -1, -1
5894   },
5895   {
5896     Xfake_acid_5,                       FALSE,  FALSE,
5897     EL_EMC_FAKE_ACID,                   -1, -1
5898   },
5899   {
5900     Xfake_acid_6,                       FALSE,  FALSE,
5901     EL_EMC_FAKE_ACID,                   -1, -1
5902   },
5903   {
5904     Xfake_acid_7,                       FALSE,  FALSE,
5905     EL_EMC_FAKE_ACID,                   -1, -1
5906   },
5907   {
5908     Xfake_acid_8,                       FALSE,  FALSE,
5909     EL_EMC_FAKE_ACID,                   -1, -1
5910   },
5911
5912   {
5913     Xfake_acid_1_player,                FALSE,  FALSE,
5914     EL_EMC_FAKE_ACID,                   -1, -1
5915   },
5916   {
5917     Xfake_acid_2_player,                FALSE,  FALSE,
5918     EL_EMC_FAKE_ACID,                   -1, -1
5919   },
5920   {
5921     Xfake_acid_3_player,                FALSE,  FALSE,
5922     EL_EMC_FAKE_ACID,                   -1, -1
5923   },
5924   {
5925     Xfake_acid_4_player,                FALSE,  FALSE,
5926     EL_EMC_FAKE_ACID,                   -1, -1
5927   },
5928   {
5929     Xfake_acid_5_player,                FALSE,  FALSE,
5930     EL_EMC_FAKE_ACID,                   -1, -1
5931   },
5932   {
5933     Xfake_acid_6_player,                FALSE,  FALSE,
5934     EL_EMC_FAKE_ACID,                   -1, -1
5935   },
5936   {
5937     Xfake_acid_7_player,                FALSE,  FALSE,
5938     EL_EMC_FAKE_ACID,                   -1, -1
5939   },
5940   {
5941     Xfake_acid_8_player,                FALSE,  FALSE,
5942     EL_EMC_FAKE_ACID,                   -1, -1
5943   },
5944
5945   {
5946     Xgrass,                             TRUE,   FALSE,
5947     EL_EMC_GRASS,                       -1, -1
5948   },
5949   {
5950     Ygrass_nB,                          FALSE,  FALSE,
5951     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
5952   },
5953   {
5954     Ygrass_eB,                          FALSE,  FALSE,
5955     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
5956   },
5957   {
5958     Ygrass_sB,                          FALSE,  FALSE,
5959     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
5960   },
5961   {
5962     Ygrass_wB,                          FALSE,  FALSE,
5963     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
5964   },
5965
5966   {
5967     Xdirt,                              TRUE,   FALSE,
5968     EL_SAND,                            -1, -1
5969   },
5970   {
5971     Ydirt_nB,                           FALSE,  FALSE,
5972     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
5973   },
5974   {
5975     Ydirt_eB,                           FALSE,  FALSE,
5976     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
5977   },
5978   {
5979     Ydirt_sB,                           FALSE,  FALSE,
5980     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
5981   },
5982   {
5983     Ydirt_wB,                           FALSE,  FALSE,
5984     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
5985   },
5986
5987   {
5988     Xandroid,                           TRUE,   FALSE,
5989     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
5990   },
5991   {
5992     Xandroid_1_n,                       FALSE,  FALSE,
5993     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5994   },
5995   {
5996     Xandroid_2_n,                       FALSE,  FALSE,
5997     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5998   },
5999   {
6000     Xandroid_1_e,                       FALSE,  FALSE,
6001     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6002   },
6003   {
6004     Xandroid_2_e,                       FALSE,  FALSE,
6005     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6006   },
6007   {
6008     Xandroid_1_w,                       FALSE,  FALSE,
6009     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6010   },
6011   {
6012     Xandroid_2_w,                       FALSE,  FALSE,
6013     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6014   },
6015   {
6016     Xandroid_1_s,                       FALSE,  FALSE,
6017     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6018   },
6019   {
6020     Xandroid_2_s,                       FALSE,  FALSE,
6021     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6022   },
6023   {
6024     Yandroid_n,                         FALSE,  FALSE,
6025     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6026   },
6027   {
6028     Yandroid_nB,                        FALSE,  TRUE,
6029     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6030   },
6031   {
6032     Yandroid_ne,                        FALSE,  FALSE,
6033     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
6034   },
6035   {
6036     Yandroid_neB,                       FALSE,  TRUE,
6037     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
6038   },
6039   {
6040     Yandroid_e,                         FALSE,  FALSE,
6041     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6042   },
6043   {
6044     Yandroid_eB,                        FALSE,  TRUE,
6045     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6046   },
6047   {
6048     Yandroid_se,                        FALSE,  FALSE,
6049     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
6050   },
6051   {
6052     Yandroid_seB,                       FALSE,  TRUE,
6053     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6054   },
6055   {
6056     Yandroid_s,                         FALSE,  FALSE,
6057     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6058   },
6059   {
6060     Yandroid_sB,                        FALSE,  TRUE,
6061     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6062   },
6063   {
6064     Yandroid_sw,                        FALSE,  FALSE,
6065     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
6066   },
6067   {
6068     Yandroid_swB,                       FALSE,  TRUE,
6069     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
6070   },
6071   {
6072     Yandroid_w,                         FALSE,  FALSE,
6073     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6074   },
6075   {
6076     Yandroid_wB,                        FALSE,  TRUE,
6077     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6078   },
6079   {
6080     Yandroid_nw,                        FALSE,  FALSE,
6081     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
6082   },
6083   {
6084     Yandroid_nwB,                       FALSE,  TRUE,
6085     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
6086   },
6087
6088   {
6089     Xeater_n,                           TRUE,   FALSE,
6090     EL_YAMYAM_UP,                       -1, -1
6091   },
6092   {
6093     Xeater_e,                           TRUE,   FALSE,
6094     EL_YAMYAM_RIGHT,                    -1, -1
6095   },
6096   {
6097     Xeater_w,                           TRUE,   FALSE,
6098     EL_YAMYAM_LEFT,                     -1, -1
6099   },
6100   {
6101     Xeater_s,                           TRUE,   FALSE,
6102     EL_YAMYAM_DOWN,                     -1, -1
6103   },
6104   {
6105     Yeater_n,                           FALSE,  FALSE,
6106     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6107   },
6108   {
6109     Yeater_nB,                          FALSE,  TRUE,
6110     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6111   },
6112   {
6113     Yeater_e,                           FALSE,  FALSE,
6114     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6115   },
6116   {
6117     Yeater_eB,                          FALSE,  TRUE,
6118     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6119   },
6120   {
6121     Yeater_s,                           FALSE,  FALSE,
6122     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6123   },
6124   {
6125     Yeater_sB,                          FALSE,  TRUE,
6126     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6127   },
6128   {
6129     Yeater_w,                           FALSE,  FALSE,
6130     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6131   },
6132   {
6133     Yeater_wB,                          FALSE,  TRUE,
6134     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6135   },
6136   {
6137     Yeater_stone,                       FALSE,  FALSE,
6138     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
6139   },
6140   {
6141     Yeater_spring,                      FALSE,  FALSE,
6142     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
6143   },
6144
6145   {
6146     Xalien,                             TRUE,   FALSE,
6147     EL_ROBOT,                           -1, -1
6148   },
6149   {
6150     Xalien_pause,                       FALSE,  FALSE,
6151     EL_ROBOT,                           -1, -1
6152   },
6153   {
6154     Yalien_n,                           FALSE,  FALSE,
6155     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6156   },
6157   {
6158     Yalien_nB,                          FALSE,  TRUE,
6159     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6160   },
6161   {
6162     Yalien_e,                           FALSE,  FALSE,
6163     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6164   },
6165   {
6166     Yalien_eB,                          FALSE,  TRUE,
6167     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6168   },
6169   {
6170     Yalien_s,                           FALSE,  FALSE,
6171     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6172   },
6173   {
6174     Yalien_sB,                          FALSE,  TRUE,
6175     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6176   },
6177   {
6178     Yalien_w,                           FALSE,  FALSE,
6179     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6180   },
6181   {
6182     Yalien_wB,                          FALSE,  TRUE,
6183     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6184   },
6185   {
6186     Yalien_stone,                       FALSE,  FALSE,
6187     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
6188   },
6189   {
6190     Yalien_spring,                      FALSE,  FALSE,
6191     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
6192   },
6193
6194   {
6195     Xbug_1_n,                           TRUE,   FALSE,
6196     EL_BUG_UP,                          -1, -1
6197   },
6198   {
6199     Xbug_1_e,                           TRUE,   FALSE,
6200     EL_BUG_RIGHT,                       -1, -1
6201   },
6202   {
6203     Xbug_1_s,                           TRUE,   FALSE,
6204     EL_BUG_DOWN,                        -1, -1
6205   },
6206   {
6207     Xbug_1_w,                           TRUE,   FALSE,
6208     EL_BUG_LEFT,                        -1, -1
6209   },
6210   {
6211     Xbug_2_n,                           FALSE,  FALSE,
6212     EL_BUG_UP,                          -1, -1
6213   },
6214   {
6215     Xbug_2_e,                           FALSE,  FALSE,
6216     EL_BUG_RIGHT,                       -1, -1
6217   },
6218   {
6219     Xbug_2_s,                           FALSE,  FALSE,
6220     EL_BUG_DOWN,                        -1, -1
6221   },
6222   {
6223     Xbug_2_w,                           FALSE,  FALSE,
6224     EL_BUG_LEFT,                        -1, -1
6225   },
6226   {
6227     Ybug_n,                             FALSE,  FALSE,
6228     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6229   },
6230   {
6231     Ybug_nB,                            FALSE,  TRUE,
6232     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6233   },
6234   {
6235     Ybug_e,                             FALSE,  FALSE,
6236     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6237   },
6238   {
6239     Ybug_eB,                            FALSE,  TRUE,
6240     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6241   },
6242   {
6243     Ybug_s,                             FALSE,  FALSE,
6244     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6245   },
6246   {
6247     Ybug_sB,                            FALSE,  TRUE,
6248     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6249   },
6250   {
6251     Ybug_w,                             FALSE,  FALSE,
6252     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6253   },
6254   {
6255     Ybug_wB,                            FALSE,  TRUE,
6256     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6257   },
6258   {
6259     Ybug_w_n,                           FALSE,  FALSE,
6260     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6261   },
6262   {
6263     Ybug_n_e,                           FALSE,  FALSE,
6264     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6265   },
6266   {
6267     Ybug_e_s,                           FALSE,  FALSE,
6268     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6269   },
6270   {
6271     Ybug_s_w,                           FALSE,  FALSE,
6272     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6273   },
6274   {
6275     Ybug_e_n,                           FALSE,  FALSE,
6276     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6277   },
6278   {
6279     Ybug_s_e,                           FALSE,  FALSE,
6280     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6281   },
6282   {
6283     Ybug_w_s,                           FALSE,  FALSE,
6284     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6285   },
6286   {
6287     Ybug_n_w,                           FALSE,  FALSE,
6288     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6289   },
6290   {
6291     Ybug_stone,                         FALSE,  FALSE,
6292     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
6293   },
6294   {
6295     Ybug_spring,                        FALSE,  FALSE,
6296     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
6297   },
6298
6299   {
6300     Xtank_1_n,                          TRUE,   FALSE,
6301     EL_SPACESHIP_UP,                    -1, -1
6302   },
6303   {
6304     Xtank_1_e,                          TRUE,   FALSE,
6305     EL_SPACESHIP_RIGHT,                 -1, -1
6306   },
6307   {
6308     Xtank_1_s,                          TRUE,   FALSE,
6309     EL_SPACESHIP_DOWN,                  -1, -1
6310   },
6311   {
6312     Xtank_1_w,                          TRUE,   FALSE,
6313     EL_SPACESHIP_LEFT,                  -1, -1
6314   },
6315   {
6316     Xtank_2_n,                          FALSE,  FALSE,
6317     EL_SPACESHIP_UP,                    -1, -1
6318   },
6319   {
6320     Xtank_2_e,                          FALSE,  FALSE,
6321     EL_SPACESHIP_RIGHT,                 -1, -1
6322   },
6323   {
6324     Xtank_2_s,                          FALSE,  FALSE,
6325     EL_SPACESHIP_DOWN,                  -1, -1
6326   },
6327   {
6328     Xtank_2_w,                          FALSE,  FALSE,
6329     EL_SPACESHIP_LEFT,                  -1, -1
6330   },
6331   {
6332     Ytank_n,                            FALSE,  FALSE,
6333     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6334   },
6335   {
6336     Ytank_nB,                           FALSE,  TRUE,
6337     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6338   },
6339   {
6340     Ytank_e,                            FALSE,  FALSE,
6341     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6342   },
6343   {
6344     Ytank_eB,                           FALSE,  TRUE,
6345     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6346   },
6347   {
6348     Ytank_s,                            FALSE,  FALSE,
6349     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6350   },
6351   {
6352     Ytank_sB,                           FALSE,  TRUE,
6353     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6354   },
6355   {
6356     Ytank_w,                            FALSE,  FALSE,
6357     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6358   },
6359   {
6360     Ytank_wB,                           FALSE,  TRUE,
6361     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6362   },
6363   {
6364     Ytank_w_n,                          FALSE,  FALSE,
6365     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6366   },
6367   {
6368     Ytank_n_e,                          FALSE,  FALSE,
6369     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6370   },
6371   {
6372     Ytank_e_s,                          FALSE,  FALSE,
6373     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6374   },
6375   {
6376     Ytank_s_w,                          FALSE,  FALSE,
6377     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6378   },
6379   {
6380     Ytank_e_n,                          FALSE,  FALSE,
6381     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6382   },
6383   {
6384     Ytank_s_e,                          FALSE,  FALSE,
6385     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6386   },
6387   {
6388     Ytank_w_s,                          FALSE,  FALSE,
6389     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6390   },
6391   {
6392     Ytank_n_w,                          FALSE,  FALSE,
6393     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6394   },
6395   {
6396     Ytank_stone,                        FALSE,  FALSE,
6397     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
6398   },
6399   {
6400     Ytank_spring,                       FALSE,  FALSE,
6401     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
6402   },
6403
6404   {
6405     Xemerald,                           TRUE,   FALSE,
6406     EL_EMERALD,                         -1, -1
6407   },
6408   {
6409     Xemerald_pause,                     FALSE,  FALSE,
6410     EL_EMERALD,                         -1, -1
6411   },
6412   {
6413     Xemerald_fall,                      FALSE,  FALSE,
6414     EL_EMERALD,                         -1, -1
6415   },
6416   {
6417     Xemerald_shine,                     FALSE,  FALSE,
6418     EL_EMERALD,                         ACTION_TWINKLING, -1
6419   },
6420   {
6421     Yemerald_s,                         FALSE,  FALSE,
6422     EL_EMERALD,                         ACTION_FALLING, -1
6423   },
6424   {
6425     Yemerald_sB,                        FALSE,  TRUE,
6426     EL_EMERALD,                         ACTION_FALLING, -1
6427   },
6428   {
6429     Yemerald_e,                         FALSE,  FALSE,
6430     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6431   },
6432   {
6433     Yemerald_eB,                        FALSE,  TRUE,
6434     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6435   },
6436   {
6437     Yemerald_w,                         FALSE,  FALSE,
6438     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6439   },
6440   {
6441     Yemerald_wB,                        FALSE,  TRUE,
6442     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6443   },
6444   {
6445     Yemerald_blank,                     FALSE,  FALSE,
6446     EL_EMERALD,                         ACTION_COLLECTING, -1
6447   },
6448
6449   {
6450     Xdiamond,                           TRUE,   FALSE,
6451     EL_DIAMOND,                         -1, -1
6452   },
6453   {
6454     Xdiamond_pause,                     FALSE,  FALSE,
6455     EL_DIAMOND,                         -1, -1
6456   },
6457   {
6458     Xdiamond_fall,                      FALSE,  FALSE,
6459     EL_DIAMOND,                         -1, -1
6460   },
6461   {
6462     Xdiamond_shine,                     FALSE,  FALSE,
6463     EL_DIAMOND,                         ACTION_TWINKLING, -1
6464   },
6465   {
6466     Ydiamond_s,                         FALSE,  FALSE,
6467     EL_DIAMOND,                         ACTION_FALLING, -1
6468   },
6469   {
6470     Ydiamond_sB,                        FALSE,  TRUE,
6471     EL_DIAMOND,                         ACTION_FALLING, -1
6472   },
6473   {
6474     Ydiamond_e,                         FALSE,  FALSE,
6475     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6476   },
6477   {
6478     Ydiamond_eB,                        FALSE,  TRUE,
6479     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6480   },
6481   {
6482     Ydiamond_w,                         FALSE,  FALSE,
6483     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6484   },
6485   {
6486     Ydiamond_wB,                        FALSE,  TRUE,
6487     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6488   },
6489   {
6490     Ydiamond_blank,                     FALSE,  FALSE,
6491     EL_DIAMOND,                         ACTION_COLLECTING, -1
6492   },
6493   {
6494     Ydiamond_stone,                     FALSE,  FALSE,
6495     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
6496   },
6497
6498   {
6499     Xstone,                             TRUE,   FALSE,
6500     EL_ROCK,                            -1, -1
6501   },
6502   {
6503     Xstone_pause,                       FALSE,  FALSE,
6504     EL_ROCK,                            -1, -1
6505   },
6506   {
6507     Xstone_fall,                        FALSE,  FALSE,
6508     EL_ROCK,                            -1, -1
6509   },
6510   {
6511     Ystone_s,                           FALSE,  FALSE,
6512     EL_ROCK,                            ACTION_FALLING, -1
6513   },
6514   {
6515     Ystone_sB,                          FALSE,  TRUE,
6516     EL_ROCK,                            ACTION_FALLING, -1
6517   },
6518   {
6519     Ystone_e,                           FALSE,  FALSE,
6520     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6521   },
6522   {
6523     Ystone_eB,                          FALSE,  TRUE,
6524     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6525   },
6526   {
6527     Ystone_w,                           FALSE,  FALSE,
6528     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6529   },
6530   {
6531     Ystone_wB,                          FALSE,  TRUE,
6532     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6533   },
6534
6535   {
6536     Xbomb,                              TRUE,   FALSE,
6537     EL_BOMB,                            -1, -1
6538   },
6539   {
6540     Xbomb_pause,                        FALSE,  FALSE,
6541     EL_BOMB,                            -1, -1
6542   },
6543   {
6544     Xbomb_fall,                         FALSE,  FALSE,
6545     EL_BOMB,                            -1, -1
6546   },
6547   {
6548     Ybomb_s,                            FALSE,  FALSE,
6549     EL_BOMB,                            ACTION_FALLING, -1
6550   },
6551   {
6552     Ybomb_sB,                           FALSE,  TRUE,
6553     EL_BOMB,                            ACTION_FALLING, -1
6554   },
6555   {
6556     Ybomb_e,                            FALSE,  FALSE,
6557     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6558   },
6559   {
6560     Ybomb_eB,                           FALSE,  TRUE,
6561     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6562   },
6563   {
6564     Ybomb_w,                            FALSE,  FALSE,
6565     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6566   },
6567   {
6568     Ybomb_wB,                           FALSE,  TRUE,
6569     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6570   },
6571   {
6572     Ybomb_blank,                        FALSE,  FALSE,
6573     EL_BOMB,                            ACTION_ACTIVATING, -1
6574   },
6575
6576   {
6577     Xnut,                               TRUE,   FALSE,
6578     EL_NUT,                             -1, -1
6579   },
6580   {
6581     Xnut_pause,                         FALSE,  FALSE,
6582     EL_NUT,                             -1, -1
6583   },
6584   {
6585     Xnut_fall,                          FALSE,  FALSE,
6586     EL_NUT,                             -1, -1
6587   },
6588   {
6589     Ynut_s,                             FALSE,  FALSE,
6590     EL_NUT,                             ACTION_FALLING, -1
6591   },
6592   {
6593     Ynut_sB,                            FALSE,  TRUE,
6594     EL_NUT,                             ACTION_FALLING, -1
6595   },
6596   {
6597     Ynut_e,                             FALSE,  FALSE,
6598     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6599   },
6600   {
6601     Ynut_eB,                            FALSE,  TRUE,
6602     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6603   },
6604   {
6605     Ynut_w,                             FALSE,  FALSE,
6606     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6607   },
6608   {
6609     Ynut_wB,                            FALSE,  TRUE,
6610     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6611   },
6612   {
6613     Ynut_stone,                         FALSE,  FALSE,
6614     EL_NUT,                             ACTION_BREAKING, -1
6615   },
6616
6617   {
6618     Xspring,                            TRUE,   FALSE,
6619     EL_SPRING,                          -1, -1
6620   },
6621   {
6622     Xspring_pause,                      FALSE,  FALSE,
6623     EL_SPRING,                          -1, -1
6624   },
6625   {
6626     Xspring_e,                          TRUE,   FALSE,
6627     EL_SPRING_RIGHT,                    -1, -1
6628   },
6629   {
6630     Xspring_w,                          TRUE,   FALSE,
6631     EL_SPRING_LEFT,                     -1, -1
6632   },
6633   {
6634     Xspring_fall,                       FALSE,  FALSE,
6635     EL_SPRING,                          -1, -1
6636   },
6637   {
6638     Yspring_s,                          FALSE,  FALSE,
6639     EL_SPRING,                          ACTION_FALLING, -1
6640   },
6641   {
6642     Yspring_sB,                         FALSE,  TRUE,
6643     EL_SPRING,                          ACTION_FALLING, -1
6644   },
6645   {
6646     Yspring_e,                          FALSE,  FALSE,
6647     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6648   },
6649   {
6650     Yspring_eB,                         FALSE,  TRUE,
6651     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6652   },
6653   {
6654     Yspring_w,                          FALSE,  FALSE,
6655     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6656   },
6657   {
6658     Yspring_wB,                         FALSE,  TRUE,
6659     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6660   },
6661   {
6662     Yspring_alien_e,                    FALSE,  FALSE,
6663     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6664   },
6665   {
6666     Yspring_alien_eB,                   FALSE,  TRUE,
6667     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6668   },
6669   {
6670     Yspring_alien_w,                    FALSE,  FALSE,
6671     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6672   },
6673   {
6674     Yspring_alien_wB,                   FALSE,  TRUE,
6675     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6676   },
6677
6678   {
6679     Xpush_emerald_e,                    FALSE,  FALSE,
6680     EL_EMERALD,                         -1, MV_BIT_RIGHT
6681   },
6682   {
6683     Xpush_emerald_w,                    FALSE,  FALSE,
6684     EL_EMERALD,                         -1, MV_BIT_LEFT
6685   },
6686   {
6687     Xpush_diamond_e,                    FALSE,  FALSE,
6688     EL_DIAMOND,                         -1, MV_BIT_RIGHT
6689   },
6690   {
6691     Xpush_diamond_w,                    FALSE,  FALSE,
6692     EL_DIAMOND,                         -1, MV_BIT_LEFT
6693   },
6694   {
6695     Xpush_stone_e,                      FALSE,  FALSE,
6696     EL_ROCK,                            -1, MV_BIT_RIGHT
6697   },
6698   {
6699     Xpush_stone_w,                      FALSE,  FALSE,
6700     EL_ROCK,                            -1, MV_BIT_LEFT
6701   },
6702   {
6703     Xpush_bomb_e,                       FALSE,  FALSE,
6704     EL_BOMB,                            -1, MV_BIT_RIGHT
6705   },
6706   {
6707     Xpush_bomb_w,                       FALSE,  FALSE,
6708     EL_BOMB,                            -1, MV_BIT_LEFT
6709   },
6710   {
6711     Xpush_nut_e,                        FALSE,  FALSE,
6712     EL_NUT,                             -1, MV_BIT_RIGHT
6713   },
6714   {
6715     Xpush_nut_w,                        FALSE,  FALSE,
6716     EL_NUT,                             -1, MV_BIT_LEFT
6717   },
6718   {
6719     Xpush_spring_e,                     FALSE,  FALSE,
6720     EL_SPRING_RIGHT,                    -1, MV_BIT_RIGHT
6721   },
6722   {
6723     Xpush_spring_w,                     FALSE,  FALSE,
6724     EL_SPRING_LEFT,                     -1, MV_BIT_LEFT
6725   },
6726
6727   {
6728     Xdynamite,                          TRUE,   FALSE,
6729     EL_EM_DYNAMITE,                     -1, -1
6730   },
6731   {
6732     Ydynamite_blank,                    FALSE,  FALSE,
6733     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6734   },
6735   {
6736     Xdynamite_1,                        TRUE,   FALSE,
6737     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6738   },
6739   {
6740     Xdynamite_2,                        FALSE,  FALSE,
6741     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6742   },
6743   {
6744     Xdynamite_3,                        FALSE,  FALSE,
6745     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6746   },
6747   {
6748     Xdynamite_4,                        FALSE,  FALSE,
6749     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6750   },
6751
6752   {
6753     Xkey_1,                             TRUE,   FALSE,
6754     EL_EM_KEY_1,                        -1, -1
6755   },
6756   {
6757     Xkey_2,                             TRUE,   FALSE,
6758     EL_EM_KEY_2,                        -1, -1
6759   },
6760   {
6761     Xkey_3,                             TRUE,   FALSE,
6762     EL_EM_KEY_3,                        -1, -1
6763   },
6764   {
6765     Xkey_4,                             TRUE,   FALSE,
6766     EL_EM_KEY_4,                        -1, -1
6767   },
6768   {
6769     Xkey_5,                             TRUE,   FALSE,
6770     EL_EMC_KEY_5,                       -1, -1
6771   },
6772   {
6773     Xkey_6,                             TRUE,   FALSE,
6774     EL_EMC_KEY_6,                       -1, -1
6775   },
6776   {
6777     Xkey_7,                             TRUE,   FALSE,
6778     EL_EMC_KEY_7,                       -1, -1
6779   },
6780   {
6781     Xkey_8,                             TRUE,   FALSE,
6782     EL_EMC_KEY_8,                       -1, -1
6783   },
6784
6785   {
6786     Xdoor_1,                            TRUE,   FALSE,
6787     EL_EM_GATE_1,                       -1, -1
6788   },
6789   {
6790     Xdoor_2,                            TRUE,   FALSE,
6791     EL_EM_GATE_2,                       -1, -1
6792   },
6793   {
6794     Xdoor_3,                            TRUE,   FALSE,
6795     EL_EM_GATE_3,                       -1, -1
6796   },
6797   {
6798     Xdoor_4,                            TRUE,   FALSE,
6799     EL_EM_GATE_4,                       -1, -1
6800   },
6801   {
6802     Xdoor_5,                            TRUE,   FALSE,
6803     EL_EMC_GATE_5,                      -1, -1
6804   },
6805   {
6806     Xdoor_6,                            TRUE,   FALSE,
6807     EL_EMC_GATE_6,                      -1, -1
6808   },
6809   {
6810     Xdoor_7,                            TRUE,   FALSE,
6811     EL_EMC_GATE_7,                      -1, -1
6812   },
6813   {
6814     Xdoor_8,                            TRUE,   FALSE,
6815     EL_EMC_GATE_8,                      -1, -1
6816   },
6817
6818   {
6819     Xfake_door_1,                       TRUE,   FALSE,
6820     EL_EM_GATE_1_GRAY,                  -1, -1
6821   },
6822   {
6823     Xfake_door_2,                       TRUE,   FALSE,
6824     EL_EM_GATE_2_GRAY,                  -1, -1
6825   },
6826   {
6827     Xfake_door_3,                       TRUE,   FALSE,
6828     EL_EM_GATE_3_GRAY,                  -1, -1
6829   },
6830   {
6831     Xfake_door_4,                       TRUE,   FALSE,
6832     EL_EM_GATE_4_GRAY,                  -1, -1
6833   },
6834   {
6835     Xfake_door_5,                       TRUE,   FALSE,
6836     EL_EMC_GATE_5_GRAY,                 -1, -1
6837   },
6838   {
6839     Xfake_door_6,                       TRUE,   FALSE,
6840     EL_EMC_GATE_6_GRAY,                 -1, -1
6841   },
6842   {
6843     Xfake_door_7,                       TRUE,   FALSE,
6844     EL_EMC_GATE_7_GRAY,                 -1, -1
6845   },
6846   {
6847     Xfake_door_8,                       TRUE,   FALSE,
6848     EL_EMC_GATE_8_GRAY,                 -1, -1
6849   },
6850
6851   {
6852     Xballoon,                           TRUE,   FALSE,
6853     EL_BALLOON,                         -1, -1
6854   },
6855   {
6856     Yballoon_n,                         FALSE,  FALSE,
6857     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6858   },
6859   {
6860     Yballoon_nB,                        FALSE,  TRUE,
6861     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6862   },
6863   {
6864     Yballoon_e,                         FALSE,  FALSE,
6865     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6866   },
6867   {
6868     Yballoon_eB,                        FALSE,  TRUE,
6869     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6870   },
6871   {
6872     Yballoon_s,                         FALSE,  FALSE,
6873     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6874   },
6875   {
6876     Yballoon_sB,                        FALSE,  TRUE,
6877     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6878   },
6879   {
6880     Yballoon_w,                         FALSE,  FALSE,
6881     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6882   },
6883   {
6884     Yballoon_wB,                        FALSE,  TRUE,
6885     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6886   },
6887
6888   {
6889     Xball_1,                            TRUE,   FALSE,
6890     EL_EMC_MAGIC_BALL,                  -1, -1
6891   },
6892   {
6893     Yball_1,                            FALSE,  FALSE,
6894     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6895   },
6896   {
6897     Xball_2,                            FALSE,  FALSE,
6898     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6899   },
6900   {
6901     Yball_2,                            FALSE,  FALSE,
6902     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6903   },
6904   {
6905     Yball_blank,                        FALSE,  FALSE,
6906     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
6907   },
6908
6909   {
6910     Xamoeba_1,                          TRUE,   FALSE,
6911     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6912   },
6913   {
6914     Xamoeba_2,                          FALSE,  FALSE,
6915     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6916   },
6917   {
6918     Xamoeba_3,                          FALSE,  FALSE,
6919     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6920   },
6921   {
6922     Xamoeba_4,                          FALSE,  FALSE,
6923     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6924   },
6925   {
6926     Xamoeba_5,                          TRUE,   FALSE,
6927     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6928   },
6929   {
6930     Xamoeba_6,                          FALSE,  FALSE,
6931     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6932   },
6933   {
6934     Xamoeba_7,                          FALSE,  FALSE,
6935     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6936   },
6937   {
6938     Xamoeba_8,                          FALSE,  FALSE,
6939     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6940   },
6941
6942   {
6943     Xdrip,                              TRUE,   FALSE,
6944     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
6945   },
6946   {
6947     Xdrip_fall,                         FALSE,  FALSE,
6948     EL_AMOEBA_DROP,                     -1, -1
6949   },
6950   {
6951     Xdrip_stretch,                      FALSE,  FALSE,
6952     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6953   },
6954   {
6955     Xdrip_stretchB,                     FALSE,  TRUE,
6956     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6957   },
6958   {
6959     Ydrip_1_s,                          FALSE,  FALSE,
6960     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6961   },
6962   {
6963     Ydrip_1_sB,                         FALSE,  TRUE,
6964     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6965   },
6966   {
6967     Ydrip_2_s,                          FALSE,  FALSE,
6968     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6969   },
6970   {
6971     Ydrip_2_sB,                         FALSE,  TRUE,
6972     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6973   },
6974
6975   {
6976     Xwonderwall,                        TRUE,   FALSE,
6977     EL_MAGIC_WALL,                      -1, -1
6978   },
6979   {
6980     Ywonderwall,                        FALSE,  FALSE,
6981     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
6982   },
6983
6984   {
6985     Xwheel,                             TRUE,   FALSE,
6986     EL_ROBOT_WHEEL,                     -1, -1
6987   },
6988   {
6989     Ywheel,                             FALSE,  FALSE,
6990     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
6991   },
6992
6993   {
6994     Xswitch,                            TRUE,   FALSE,
6995     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
6996   },
6997   {
6998     Yswitch,                            FALSE,  FALSE,
6999     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
7000   },
7001
7002   {
7003     Xbumper,                            TRUE,   FALSE,
7004     EL_EMC_SPRING_BUMPER,               -1, -1
7005   },
7006   {
7007     Ybumper,                            FALSE,  FALSE,
7008     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
7009   },
7010
7011   {
7012     Xacid_nw,                           TRUE,   FALSE,
7013     EL_ACID_POOL_TOPLEFT,               -1, -1
7014   },
7015   {
7016     Xacid_ne,                           TRUE,   FALSE,
7017     EL_ACID_POOL_TOPRIGHT,              -1, -1
7018   },
7019   {
7020     Xacid_sw,                           TRUE,   FALSE,
7021     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
7022   },
7023   {
7024     Xacid_s,                            TRUE,   FALSE,
7025     EL_ACID_POOL_BOTTOM,                -1, -1
7026   },
7027   {
7028     Xacid_se,                           TRUE,   FALSE,
7029     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
7030   },
7031
7032   {
7033     Xfake_blank,                        TRUE,   FALSE,
7034     EL_INVISIBLE_WALL,                  -1, -1
7035   },
7036   {
7037     Yfake_blank,                        FALSE,  FALSE,
7038     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
7039   },
7040
7041   {
7042     Xfake_grass,                        TRUE,   FALSE,
7043     EL_EMC_FAKE_GRASS,                  -1, -1
7044   },
7045   {
7046     Yfake_grass,                        FALSE,  FALSE,
7047     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
7048   },
7049
7050   {
7051     Xfake_amoeba,                       TRUE,   FALSE,
7052     EL_EMC_DRIPPER,                     -1, -1
7053   },
7054   {
7055     Yfake_amoeba,                       FALSE,  FALSE,
7056     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
7057   },
7058
7059   {
7060     Xlenses,                            TRUE,   FALSE,
7061     EL_EMC_LENSES,                      -1, -1
7062   },
7063
7064   {
7065     Xmagnify,                           TRUE,   FALSE,
7066     EL_EMC_MAGNIFIER,                   -1, -1
7067   },
7068
7069   {
7070     Xsand,                              TRUE,   FALSE,
7071     EL_QUICKSAND_EMPTY,                 -1, -1
7072   },
7073   {
7074     Xsand_stone,                        TRUE,   FALSE,
7075     EL_QUICKSAND_FULL,                  -1, -1
7076   },
7077   {
7078     Xsand_stonein_1,                    FALSE,  TRUE,
7079     EL_ROCK,                            ACTION_FILLING, -1
7080   },
7081   {
7082     Xsand_stonein_2,                    FALSE,  TRUE,
7083     EL_ROCK,                            ACTION_FILLING, -1
7084   },
7085   {
7086     Xsand_stonein_3,                    FALSE,  TRUE,
7087     EL_ROCK,                            ACTION_FILLING, -1
7088   },
7089   {
7090     Xsand_stonein_4,                    FALSE,  TRUE,
7091     EL_ROCK,                            ACTION_FILLING, -1
7092   },
7093   {
7094     Xsand_sandstone_1,                  FALSE,  FALSE,
7095     EL_QUICKSAND_FILLING,               -1, -1
7096   },
7097   {
7098     Xsand_sandstone_2,                  FALSE,  FALSE,
7099     EL_QUICKSAND_FILLING,               -1, -1
7100   },
7101   {
7102     Xsand_sandstone_3,                  FALSE,  FALSE,
7103     EL_QUICKSAND_FILLING,               -1, -1
7104   },
7105   {
7106     Xsand_sandstone_4,                  FALSE,  FALSE,
7107     EL_QUICKSAND_FILLING,               -1, -1
7108   },
7109   {
7110     Xsand_stonesand_1,                  FALSE,  FALSE,
7111     EL_QUICKSAND_EMPTYING,              -1, -1
7112   },
7113   {
7114     Xsand_stonesand_2,                  FALSE,  FALSE,
7115     EL_QUICKSAND_EMPTYING,              -1, -1
7116   },
7117   {
7118     Xsand_stonesand_3,                  FALSE,  FALSE,
7119     EL_QUICKSAND_EMPTYING,              -1, -1
7120   },
7121   {
7122     Xsand_stonesand_4,                  FALSE,  FALSE,
7123     EL_QUICKSAND_EMPTYING,              -1, -1
7124   },
7125   {
7126     Xsand_stoneout_1,                   FALSE,  FALSE,
7127     EL_ROCK,                            ACTION_EMPTYING, -1
7128   },
7129   {
7130     Xsand_stoneout_2,                   FALSE,  FALSE,
7131     EL_ROCK,                            ACTION_EMPTYING, -1
7132   },
7133   {
7134     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
7135     EL_QUICKSAND_EMPTYING,              -1, -1
7136   },
7137   {
7138     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
7139     EL_QUICKSAND_EMPTYING,              -1, -1
7140   },
7141
7142   {
7143     Xslide_ns,                          TRUE,   FALSE,
7144     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
7145   },
7146   {
7147     Yslide_ns_blank,                    FALSE,  FALSE,
7148     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
7149   },
7150   {
7151     Xslide_ew,                          TRUE,   FALSE,
7152     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
7153   },
7154   {
7155     Yslide_ew_blank,                    FALSE,  FALSE,
7156     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
7157   },
7158
7159   {
7160     Xwind_n,                            TRUE,   FALSE,
7161     EL_BALLOON_SWITCH_UP,               -1, -1
7162   },
7163   {
7164     Xwind_e,                            TRUE,   FALSE,
7165     EL_BALLOON_SWITCH_RIGHT,            -1, -1
7166   },
7167   {
7168     Xwind_s,                            TRUE,   FALSE,
7169     EL_BALLOON_SWITCH_DOWN,             -1, -1
7170   },
7171   {
7172     Xwind_w,                            TRUE,   FALSE,
7173     EL_BALLOON_SWITCH_LEFT,             -1, -1
7174   },
7175   {
7176     Xwind_any,                          TRUE,   FALSE,
7177     EL_BALLOON_SWITCH_ANY,              -1, -1
7178   },
7179   {
7180     Xwind_stop,                         TRUE,   FALSE,
7181     EL_BALLOON_SWITCH_NONE,             -1, -1
7182   },
7183
7184   {
7185     Xexit,                              TRUE,   FALSE,
7186     EL_EM_EXIT_CLOSED,                  -1, -1
7187   },
7188   {
7189     Xexit_1,                            TRUE,   FALSE,
7190     EL_EM_EXIT_OPEN,                    -1, -1
7191   },
7192   {
7193     Xexit_2,                            FALSE,  FALSE,
7194     EL_EM_EXIT_OPEN,                    -1, -1
7195   },
7196   {
7197     Xexit_3,                            FALSE,  FALSE,
7198     EL_EM_EXIT_OPEN,                    -1, -1
7199   },
7200
7201   {
7202     Xpause,                             FALSE,  FALSE,
7203     EL_EMPTY,                           -1, -1
7204   },
7205
7206   {
7207     Xwall_1,                            TRUE,   FALSE,
7208     EL_WALL,                            -1, -1
7209   },
7210   {
7211     Xwall_2,                            TRUE,   FALSE,
7212     EL_EMC_WALL_14,                     -1, -1
7213   },
7214   {
7215     Xwall_3,                            TRUE,   FALSE,
7216     EL_EMC_WALL_15,                     -1, -1
7217   },
7218   {
7219     Xwall_4,                            TRUE,   FALSE,
7220     EL_EMC_WALL_16,                     -1, -1
7221   },
7222
7223   {
7224     Xroundwall_1,                       TRUE,   FALSE,
7225     EL_WALL_SLIPPERY,                   -1, -1
7226   },
7227   {
7228     Xroundwall_2,                       TRUE,   FALSE,
7229     EL_EMC_WALL_SLIPPERY_2,             -1, -1
7230   },
7231   {
7232     Xroundwall_3,                       TRUE,   FALSE,
7233     EL_EMC_WALL_SLIPPERY_3,             -1, -1
7234   },
7235   {
7236     Xroundwall_4,                       TRUE,   FALSE,
7237     EL_EMC_WALL_SLIPPERY_4,             -1, -1
7238   },
7239
7240   {
7241     Xsteel_1,                           TRUE,   FALSE,
7242     EL_STEELWALL,                       -1, -1
7243   },
7244   {
7245     Xsteel_2,                           TRUE,   FALSE,
7246     EL_EMC_STEELWALL_2,                 -1, -1
7247   },
7248   {
7249     Xsteel_3,                           TRUE,   FALSE,
7250     EL_EMC_STEELWALL_3,                 -1, -1
7251   },
7252   {
7253     Xsteel_4,                           TRUE,   FALSE,
7254     EL_EMC_STEELWALL_4,                 -1, -1
7255   },
7256
7257   {
7258     Xdecor_1,                           TRUE,   FALSE,
7259     EL_EMC_WALL_8,                      -1, -1
7260   },
7261   {
7262     Xdecor_2,                           TRUE,   FALSE,
7263     EL_EMC_WALL_6,                      -1, -1
7264   },
7265   {
7266     Xdecor_3,                           TRUE,   FALSE,
7267     EL_EMC_WALL_4,                      -1, -1
7268   },
7269   {
7270     Xdecor_4,                           TRUE,   FALSE,
7271     EL_EMC_WALL_7,                      -1, -1
7272   },
7273   {
7274     Xdecor_5,                           TRUE,   FALSE,
7275     EL_EMC_WALL_5,                      -1, -1
7276   },
7277   {
7278     Xdecor_6,                           TRUE,   FALSE,
7279     EL_EMC_WALL_9,                      -1, -1
7280   },
7281   {
7282     Xdecor_7,                           TRUE,   FALSE,
7283     EL_EMC_WALL_10,                     -1, -1
7284   },
7285   {
7286     Xdecor_8,                           TRUE,   FALSE,
7287     EL_EMC_WALL_1,                      -1, -1
7288   },
7289   {
7290     Xdecor_9,                           TRUE,   FALSE,
7291     EL_EMC_WALL_2,                      -1, -1
7292   },
7293   {
7294     Xdecor_10,                          TRUE,   FALSE,
7295     EL_EMC_WALL_3,                      -1, -1
7296   },
7297   {
7298     Xdecor_11,                          TRUE,   FALSE,
7299     EL_EMC_WALL_11,                     -1, -1
7300   },
7301   {
7302     Xdecor_12,                          TRUE,   FALSE,
7303     EL_EMC_WALL_12,                     -1, -1
7304   },
7305
7306   {
7307     Xalpha_0,                           TRUE,   FALSE,
7308     EL_CHAR('0'),                       -1, -1
7309   },
7310   {
7311     Xalpha_1,                           TRUE,   FALSE,
7312     EL_CHAR('1'),                       -1, -1
7313   },
7314   {
7315     Xalpha_2,                           TRUE,   FALSE,
7316     EL_CHAR('2'),                       -1, -1
7317   },
7318   {
7319     Xalpha_3,                           TRUE,   FALSE,
7320     EL_CHAR('3'),                       -1, -1
7321   },
7322   {
7323     Xalpha_4,                           TRUE,   FALSE,
7324     EL_CHAR('4'),                       -1, -1
7325   },
7326   {
7327     Xalpha_5,                           TRUE,   FALSE,
7328     EL_CHAR('5'),                       -1, -1
7329   },
7330   {
7331     Xalpha_6,                           TRUE,   FALSE,
7332     EL_CHAR('6'),                       -1, -1
7333   },
7334   {
7335     Xalpha_7,                           TRUE,   FALSE,
7336     EL_CHAR('7'),                       -1, -1
7337   },
7338   {
7339     Xalpha_8,                           TRUE,   FALSE,
7340     EL_CHAR('8'),                       -1, -1
7341   },
7342   {
7343     Xalpha_9,                           TRUE,   FALSE,
7344     EL_CHAR('9'),                       -1, -1
7345   },
7346   {
7347     Xalpha_excla,                       TRUE,   FALSE,
7348     EL_CHAR('!'),                       -1, -1
7349   },
7350   {
7351     Xalpha_apost,                       TRUE,   FALSE,
7352     EL_CHAR('\''),                      -1, -1
7353   },
7354   {
7355     Xalpha_comma,                       TRUE,   FALSE,
7356     EL_CHAR(','),                       -1, -1
7357   },
7358   {
7359     Xalpha_minus,                       TRUE,   FALSE,
7360     EL_CHAR('-'),                       -1, -1
7361   },
7362   {
7363     Xalpha_perio,                       TRUE,   FALSE,
7364     EL_CHAR('.'),                       -1, -1
7365   },
7366   {
7367     Xalpha_colon,                       TRUE,   FALSE,
7368     EL_CHAR(':'),                       -1, -1
7369   },
7370   {
7371     Xalpha_quest,                       TRUE,   FALSE,
7372     EL_CHAR('?'),                       -1, -1
7373   },
7374   {
7375     Xalpha_a,                           TRUE,   FALSE,
7376     EL_CHAR('A'),                       -1, -1
7377   },
7378   {
7379     Xalpha_b,                           TRUE,   FALSE,
7380     EL_CHAR('B'),                       -1, -1
7381   },
7382   {
7383     Xalpha_c,                           TRUE,   FALSE,
7384     EL_CHAR('C'),                       -1, -1
7385   },
7386   {
7387     Xalpha_d,                           TRUE,   FALSE,
7388     EL_CHAR('D'),                       -1, -1
7389   },
7390   {
7391     Xalpha_e,                           TRUE,   FALSE,
7392     EL_CHAR('E'),                       -1, -1
7393   },
7394   {
7395     Xalpha_f,                           TRUE,   FALSE,
7396     EL_CHAR('F'),                       -1, -1
7397   },
7398   {
7399     Xalpha_g,                           TRUE,   FALSE,
7400     EL_CHAR('G'),                       -1, -1
7401   },
7402   {
7403     Xalpha_h,                           TRUE,   FALSE,
7404     EL_CHAR('H'),                       -1, -1
7405   },
7406   {
7407     Xalpha_i,                           TRUE,   FALSE,
7408     EL_CHAR('I'),                       -1, -1
7409   },
7410   {
7411     Xalpha_j,                           TRUE,   FALSE,
7412     EL_CHAR('J'),                       -1, -1
7413   },
7414   {
7415     Xalpha_k,                           TRUE,   FALSE,
7416     EL_CHAR('K'),                       -1, -1
7417   },
7418   {
7419     Xalpha_l,                           TRUE,   FALSE,
7420     EL_CHAR('L'),                       -1, -1
7421   },
7422   {
7423     Xalpha_m,                           TRUE,   FALSE,
7424     EL_CHAR('M'),                       -1, -1
7425   },
7426   {
7427     Xalpha_n,                           TRUE,   FALSE,
7428     EL_CHAR('N'),                       -1, -1
7429   },
7430   {
7431     Xalpha_o,                           TRUE,   FALSE,
7432     EL_CHAR('O'),                       -1, -1
7433   },
7434   {
7435     Xalpha_p,                           TRUE,   FALSE,
7436     EL_CHAR('P'),                       -1, -1
7437   },
7438   {
7439     Xalpha_q,                           TRUE,   FALSE,
7440     EL_CHAR('Q'),                       -1, -1
7441   },
7442   {
7443     Xalpha_r,                           TRUE,   FALSE,
7444     EL_CHAR('R'),                       -1, -1
7445   },
7446   {
7447     Xalpha_s,                           TRUE,   FALSE,
7448     EL_CHAR('S'),                       -1, -1
7449   },
7450   {
7451     Xalpha_t,                           TRUE,   FALSE,
7452     EL_CHAR('T'),                       -1, -1
7453   },
7454   {
7455     Xalpha_u,                           TRUE,   FALSE,
7456     EL_CHAR('U'),                       -1, -1
7457   },
7458   {
7459     Xalpha_v,                           TRUE,   FALSE,
7460     EL_CHAR('V'),                       -1, -1
7461   },
7462   {
7463     Xalpha_w,                           TRUE,   FALSE,
7464     EL_CHAR('W'),                       -1, -1
7465   },
7466   {
7467     Xalpha_x,                           TRUE,   FALSE,
7468     EL_CHAR('X'),                       -1, -1
7469   },
7470   {
7471     Xalpha_y,                           TRUE,   FALSE,
7472     EL_CHAR('Y'),                       -1, -1
7473   },
7474   {
7475     Xalpha_z,                           TRUE,   FALSE,
7476     EL_CHAR('Z'),                       -1, -1
7477   },
7478   {
7479     Xalpha_arrow_e,                     TRUE,   FALSE,
7480     EL_CHAR('>'),                       -1, -1
7481   },
7482   {
7483     Xalpha_arrow_w,                     TRUE,   FALSE,
7484     EL_CHAR('<'),                       -1, -1
7485   },
7486   {
7487     Xalpha_copyr,                       TRUE,   FALSE,
7488     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
7489   },
7490
7491   {
7492     Ykey_1_blank,                       FALSE,  FALSE,
7493     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
7494   },
7495   {
7496     Ykey_2_blank,                       FALSE,  FALSE,
7497     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
7498   },
7499   {
7500     Ykey_3_blank,                       FALSE,  FALSE,
7501     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
7502   },
7503   {
7504     Ykey_4_blank,                       FALSE,  FALSE,
7505     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
7506   },
7507   {
7508     Ykey_5_blank,                       FALSE,  FALSE,
7509     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
7510   },
7511   {
7512     Ykey_6_blank,                       FALSE,  FALSE,
7513     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
7514   },
7515   {
7516     Ykey_7_blank,                       FALSE,  FALSE,
7517     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
7518   },
7519   {
7520     Ykey_8_blank,                       FALSE,  FALSE,
7521     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
7522   },
7523   {
7524     Ylenses_blank,                      FALSE,  FALSE,
7525     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
7526   },
7527   {
7528     Ymagnify_blank,                     FALSE,  FALSE,
7529     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
7530   },
7531   {
7532     Ygrass_blank,                       FALSE,  FALSE,
7533     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
7534   },
7535   {
7536     Ydirt_blank,                        FALSE,  FALSE,
7537     EL_SAND,                            ACTION_SNAPPING, -1
7538   },
7539
7540   {
7541     -1,                                 FALSE,  FALSE,
7542     -1,                                 -1, -1
7543   }
7544 };
7545
7546 static struct Mapping_EM_to_RND_player
7547 {
7548   int action_em;
7549   int player_nr;
7550
7551   int element_rnd;
7552   int action;
7553   int direction;
7554 }
7555 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7556 {
7557   {
7558     PLY_walk_n,                         0,
7559     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
7560   },
7561   {
7562     PLY_walk_e,                         0,
7563     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
7564   },
7565   {
7566     PLY_walk_s,                         0,
7567     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
7568   },
7569   {
7570     PLY_walk_w,                         0,
7571     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
7572   },
7573   {
7574     PLY_push_n,                         0,
7575     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
7576   },
7577   {
7578     PLY_push_e,                         0,
7579     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
7580   },
7581   {
7582     PLY_push_s,                         0,
7583     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
7584   },
7585   {
7586     PLY_push_w,                         0,
7587     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
7588   },
7589   {
7590     PLY_shoot_n,                        0,
7591     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
7592   },
7593   {
7594     PLY_shoot_e,                        0,
7595     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7596   },
7597   {
7598     PLY_shoot_s,                        0,
7599     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
7600   },
7601   {
7602     PLY_shoot_w,                        0,
7603     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
7604   },
7605   {
7606     PLY_walk_n,                         1,
7607     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
7608   },
7609   {
7610     PLY_walk_e,                         1,
7611     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
7612   },
7613   {
7614     PLY_walk_s,                         1,
7615     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
7616   },
7617   {
7618     PLY_walk_w,                         1,
7619     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
7620   },
7621   {
7622     PLY_push_n,                         1,
7623     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
7624   },
7625   {
7626     PLY_push_e,                         1,
7627     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
7628   },
7629   {
7630     PLY_push_s,                         1,
7631     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
7632   },
7633   {
7634     PLY_push_w,                         1,
7635     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
7636   },
7637   {
7638     PLY_shoot_n,                        1,
7639     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
7640   },
7641   {
7642     PLY_shoot_e,                        1,
7643     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7644   },
7645   {
7646     PLY_shoot_s,                        1,
7647     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
7648   },
7649   {
7650     PLY_shoot_w,                        1,
7651     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
7652   },
7653   {
7654     PLY_still,                          0,
7655     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
7656   },
7657   {
7658     PLY_still,                          1,
7659     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
7660   },
7661   {
7662     PLY_walk_n,                         2,
7663     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
7664   },
7665   {
7666     PLY_walk_e,                         2,
7667     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
7668   },
7669   {
7670     PLY_walk_s,                         2,
7671     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
7672   },
7673   {
7674     PLY_walk_w,                         2,
7675     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
7676   },
7677   {
7678     PLY_push_n,                         2,
7679     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
7680   },
7681   {
7682     PLY_push_e,                         2,
7683     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
7684   },
7685   {
7686     PLY_push_s,                         2,
7687     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
7688   },
7689   {
7690     PLY_push_w,                         2,
7691     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
7692   },
7693   {
7694     PLY_shoot_n,                        2,
7695     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
7696   },
7697   {
7698     PLY_shoot_e,                        2,
7699     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7700   },
7701   {
7702     PLY_shoot_s,                        2,
7703     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
7704   },
7705   {
7706     PLY_shoot_w,                        2,
7707     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
7708   },
7709   {
7710     PLY_walk_n,                         3,
7711     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
7712   },
7713   {
7714     PLY_walk_e,                         3,
7715     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
7716   },
7717   {
7718     PLY_walk_s,                         3,
7719     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
7720   },
7721   {
7722     PLY_walk_w,                         3,
7723     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
7724   },
7725   {
7726     PLY_push_n,                         3,
7727     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
7728   },
7729   {
7730     PLY_push_e,                         3,
7731     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
7732   },
7733   {
7734     PLY_push_s,                         3,
7735     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
7736   },
7737   {
7738     PLY_push_w,                         3,
7739     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
7740   },
7741   {
7742     PLY_shoot_n,                        3,
7743     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
7744   },
7745   {
7746     PLY_shoot_e,                        3,
7747     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7748   },
7749   {
7750     PLY_shoot_s,                        3,
7751     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
7752   },
7753   {
7754     PLY_shoot_w,                        3,
7755     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
7756   },
7757   {
7758     PLY_still,                          2,
7759     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
7760   },
7761   {
7762     PLY_still,                          3,
7763     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7764   },
7765
7766   {
7767     -1,                                 -1,
7768     -1,                                 -1, -1
7769   }
7770 };
7771
7772 int map_element_RND_to_EM_cave(int element_rnd)
7773 {
7774   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7775   static boolean mapping_initialized = FALSE;
7776
7777   if (!mapping_initialized)
7778   {
7779     int i;
7780
7781     // return "Xalpha_quest" for all undefined elements in mapping array
7782     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7783       mapping_RND_to_EM[i] = Xalpha_quest;
7784
7785     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7786       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7787         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7788           em_object_mapping_list[i].element_em;
7789
7790     mapping_initialized = TRUE;
7791   }
7792
7793   if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7794   {
7795     Warn("invalid RND level element %d", element_rnd);
7796
7797     return EL_UNKNOWN;
7798   }
7799
7800   return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7801 }
7802
7803 int map_element_EM_to_RND_cave(int element_em_cave)
7804 {
7805   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7806   static boolean mapping_initialized = FALSE;
7807
7808   if (!mapping_initialized)
7809   {
7810     int i;
7811
7812     // return "EL_UNKNOWN" for all undefined elements in mapping array
7813     for (i = 0; i < GAME_TILE_MAX; i++)
7814       mapping_EM_to_RND[i] = EL_UNKNOWN;
7815
7816     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7817       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7818         em_object_mapping_list[i].element_rnd;
7819
7820     mapping_initialized = TRUE;
7821   }
7822
7823   if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7824   {
7825     Warn("invalid EM cave element %d", element_em_cave);
7826
7827     return EL_UNKNOWN;
7828   }
7829
7830   return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7831 }
7832
7833 int map_element_EM_to_RND_game(int element_em_game)
7834 {
7835   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7836   static boolean mapping_initialized = FALSE;
7837
7838   if (!mapping_initialized)
7839   {
7840     int i;
7841
7842     // return "EL_UNKNOWN" for all undefined elements in mapping array
7843     for (i = 0; i < GAME_TILE_MAX; i++)
7844       mapping_EM_to_RND[i] = EL_UNKNOWN;
7845
7846     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7847       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7848         em_object_mapping_list[i].element_rnd;
7849
7850     mapping_initialized = TRUE;
7851   }
7852
7853   if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7854   {
7855     Warn("invalid EM game element %d", element_em_game);
7856
7857     return EL_UNKNOWN;
7858   }
7859
7860   return mapping_EM_to_RND[element_em_game];
7861 }
7862
7863 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7864 {
7865   struct LevelInfo_EM *level_em = level->native_em_level;
7866   struct CAVE *cav = level_em->cav;
7867   int i, j;
7868
7869   for (i = 0; i < GAME_TILE_MAX; i++)
7870     cav->android_array[i] = Cblank;
7871
7872   for (i = 0; i < level->num_android_clone_elements; i++)
7873   {
7874     int element_rnd = level->android_clone_element[i];
7875     int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7876
7877     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7878       if (em_object_mapping_list[j].element_rnd == element_rnd)
7879         cav->android_array[em_object_mapping_list[j].element_em] =
7880           element_em_cave;
7881   }
7882 }
7883
7884 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7885 {
7886   struct LevelInfo_EM *level_em = level->native_em_level;
7887   struct CAVE *cav = level_em->cav;
7888   int i, j;
7889
7890   level->num_android_clone_elements = 0;
7891
7892   for (i = 0; i < GAME_TILE_MAX; i++)
7893   {
7894     int element_em_cave = cav->android_array[i];
7895     int element_rnd;
7896     boolean element_found = FALSE;
7897
7898     if (element_em_cave == Cblank)
7899       continue;
7900
7901     element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7902
7903     for (j = 0; j < level->num_android_clone_elements; j++)
7904       if (level->android_clone_element[j] == element_rnd)
7905         element_found = TRUE;
7906
7907     if (!element_found)
7908     {
7909       level->android_clone_element[level->num_android_clone_elements++] =
7910         element_rnd;
7911
7912       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7913         break;
7914     }
7915   }
7916
7917   if (level->num_android_clone_elements == 0)
7918   {
7919     level->num_android_clone_elements = 1;
7920     level->android_clone_element[0] = EL_EMPTY;
7921   }
7922 }
7923
7924 int map_direction_RND_to_EM(int direction)
7925 {
7926   return (direction == MV_UP    ? 0 :
7927           direction == MV_RIGHT ? 1 :
7928           direction == MV_DOWN  ? 2 :
7929           direction == MV_LEFT  ? 3 :
7930           -1);
7931 }
7932
7933 int map_direction_EM_to_RND(int direction)
7934 {
7935   return (direction == 0 ? MV_UP    :
7936           direction == 1 ? MV_RIGHT :
7937           direction == 2 ? MV_DOWN  :
7938           direction == 3 ? MV_LEFT  :
7939           MV_NONE);
7940 }
7941
7942 int map_element_RND_to_SP(int element_rnd)
7943 {
7944   int element_sp = 0x20;        // map unknown elements to yellow "hardware"
7945
7946   if (element_rnd >= EL_SP_START &&
7947       element_rnd <= EL_SP_END)
7948     element_sp = element_rnd - EL_SP_START;
7949   else if (element_rnd == EL_EMPTY_SPACE)
7950     element_sp = 0x00;
7951   else if (element_rnd == EL_INVISIBLE_WALL)
7952     element_sp = 0x28;
7953
7954   return element_sp;
7955 }
7956
7957 int map_element_SP_to_RND(int element_sp)
7958 {
7959   int element_rnd = EL_UNKNOWN;
7960
7961   if (element_sp >= 0x00 &&
7962       element_sp <= 0x27)
7963     element_rnd = EL_SP_START + element_sp;
7964   else if (element_sp == 0x28)
7965     element_rnd = EL_INVISIBLE_WALL;
7966
7967   return element_rnd;
7968 }
7969
7970 int map_action_SP_to_RND(int action_sp)
7971 {
7972   switch (action_sp)
7973   {
7974     case actActive:             return ACTION_ACTIVE;
7975     case actImpact:             return ACTION_IMPACT;
7976     case actExploding:          return ACTION_EXPLODING;
7977     case actDigging:            return ACTION_DIGGING;
7978     case actSnapping:           return ACTION_SNAPPING;
7979     case actCollecting:         return ACTION_COLLECTING;
7980     case actPassing:            return ACTION_PASSING;
7981     case actPushing:            return ACTION_PUSHING;
7982     case actDropping:           return ACTION_DROPPING;
7983
7984     default:                    return ACTION_DEFAULT;
7985   }
7986 }
7987
7988 int map_element_RND_to_MM(int element_rnd)
7989 {
7990   return (element_rnd >= EL_MM_START_1 &&
7991           element_rnd <= EL_MM_END_1 ?
7992           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7993
7994           element_rnd >= EL_MM_START_2 &&
7995           element_rnd <= EL_MM_END_2 ?
7996           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7997
7998           element_rnd >= EL_CHAR_START &&
7999           element_rnd <= EL_CHAR_END ?
8000           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8001
8002           element_rnd >= EL_MM_RUNTIME_START &&
8003           element_rnd <= EL_MM_RUNTIME_END ?
8004           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8005
8006           element_rnd >= EL_MM_DUMMY_START &&
8007           element_rnd <= EL_MM_DUMMY_END ?
8008           EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8009
8010           EL_MM_EMPTY_NATIVE);
8011 }
8012
8013 int map_element_MM_to_RND(int element_mm)
8014 {
8015   return (element_mm == EL_MM_EMPTY_NATIVE ||
8016           element_mm == EL_DF_EMPTY_NATIVE ?
8017           EL_EMPTY :
8018
8019           element_mm >= EL_MM_START_1_NATIVE &&
8020           element_mm <= EL_MM_END_1_NATIVE ?
8021           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8022
8023           element_mm >= EL_MM_START_2_NATIVE &&
8024           element_mm <= EL_MM_END_2_NATIVE ?
8025           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8026
8027           element_mm >= EL_MM_CHAR_START_NATIVE &&
8028           element_mm <= EL_MM_CHAR_END_NATIVE ?
8029           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8030
8031           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8032           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8033           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8034
8035           element_mm >= EL_MM_DUMMY_START_NATIVE &&
8036           element_mm <= EL_MM_DUMMY_END_NATIVE ?
8037           EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8038
8039           EL_EMPTY);
8040 }
8041
8042 int map_action_MM_to_RND(int action_mm)
8043 {
8044   // all MM actions are defined to exactly match their RND counterparts
8045   return action_mm;
8046 }
8047
8048 int map_sound_MM_to_RND(int sound_mm)
8049 {
8050   switch (sound_mm)
8051   {
8052     case SND_MM_GAME_LEVELTIME_CHARGING:
8053       return SND_GAME_LEVELTIME_CHARGING;
8054
8055     case SND_MM_GAME_HEALTH_CHARGING:
8056       return SND_GAME_HEALTH_CHARGING;
8057
8058     default:
8059       return SND_UNDEFINED;
8060   }
8061 }
8062
8063 int map_mm_wall_element(int element)
8064 {
8065   return (element >= EL_MM_STEEL_WALL_START &&
8066           element <= EL_MM_STEEL_WALL_END ?
8067           EL_MM_STEEL_WALL :
8068
8069           element >= EL_MM_WOODEN_WALL_START &&
8070           element <= EL_MM_WOODEN_WALL_END ?
8071           EL_MM_WOODEN_WALL :
8072
8073           element >= EL_MM_ICE_WALL_START &&
8074           element <= EL_MM_ICE_WALL_END ?
8075           EL_MM_ICE_WALL :
8076
8077           element >= EL_MM_AMOEBA_WALL_START &&
8078           element <= EL_MM_AMOEBA_WALL_END ?
8079           EL_MM_AMOEBA_WALL :
8080
8081           element >= EL_DF_STEEL_WALL_START &&
8082           element <= EL_DF_STEEL_WALL_END ?
8083           EL_DF_STEEL_WALL :
8084
8085           element >= EL_DF_WOODEN_WALL_START &&
8086           element <= EL_DF_WOODEN_WALL_END ?
8087           EL_DF_WOODEN_WALL :
8088
8089           element);
8090 }
8091
8092 int map_mm_wall_element_editor(int element)
8093 {
8094   switch (element)
8095   {
8096     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
8097     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
8098     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
8099     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
8100     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
8101     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
8102
8103     default:                    return element;
8104   }
8105 }
8106
8107 int get_next_element(int element)
8108 {
8109   switch (element)
8110   {
8111     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
8112     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
8113     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
8114     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
8115     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
8116     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
8117     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
8118     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
8119     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
8120     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
8121     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
8122
8123     default:                            return element;
8124   }
8125 }
8126
8127 int el2img_mm(int element_mm)
8128 {
8129   return el2img(map_element_MM_to_RND(element_mm));
8130 }
8131
8132 int el_act_dir2img(int element, int action, int direction)
8133 {
8134   element = GFX_ELEMENT(element);
8135   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8136
8137   // direction_graphic[][] == graphic[] for undefined direction graphics
8138   return element_info[element].direction_graphic[action][direction];
8139 }
8140
8141 static int el_act_dir2crm(int element, int action, int direction)
8142 {
8143   element = GFX_ELEMENT(element);
8144   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8145
8146   // direction_graphic[][] == graphic[] for undefined direction graphics
8147   return element_info[element].direction_crumbled[action][direction];
8148 }
8149
8150 int el_act2img(int element, int action)
8151 {
8152   element = GFX_ELEMENT(element);
8153
8154   return element_info[element].graphic[action];
8155 }
8156
8157 int el_act2crm(int element, int action)
8158 {
8159   element = GFX_ELEMENT(element);
8160
8161   return element_info[element].crumbled[action];
8162 }
8163
8164 int el_dir2img(int element, int direction)
8165 {
8166   element = GFX_ELEMENT(element);
8167
8168   return el_act_dir2img(element, ACTION_DEFAULT, direction);
8169 }
8170
8171 int el2baseimg(int element)
8172 {
8173   return element_info[element].graphic[ACTION_DEFAULT];
8174 }
8175
8176 int el2img(int element)
8177 {
8178   element = GFX_ELEMENT(element);
8179
8180   return element_info[element].graphic[ACTION_DEFAULT];
8181 }
8182
8183 int el2edimg(int element)
8184 {
8185   element = GFX_ELEMENT(element);
8186
8187   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8188 }
8189
8190 int el2preimg(int element)
8191 {
8192   element = GFX_ELEMENT(element);
8193
8194   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8195 }
8196
8197 int el2panelimg(int element)
8198 {
8199   element = GFX_ELEMENT(element);
8200
8201   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8202 }
8203
8204 int font2baseimg(int font_nr)
8205 {
8206   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8207 }
8208
8209 int getBeltNrFromBeltElement(int element)
8210 {
8211   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8212           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8213           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8214 }
8215
8216 int getBeltNrFromBeltActiveElement(int element)
8217 {
8218   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8219           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8220           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8221 }
8222
8223 int getBeltNrFromBeltSwitchElement(int element)
8224 {
8225   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8226           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8227           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8228 }
8229
8230 int getBeltDirNrFromBeltElement(int element)
8231 {
8232   static int belt_base_element[4] =
8233   {
8234     EL_CONVEYOR_BELT_1_LEFT,
8235     EL_CONVEYOR_BELT_2_LEFT,
8236     EL_CONVEYOR_BELT_3_LEFT,
8237     EL_CONVEYOR_BELT_4_LEFT
8238   };
8239
8240   int belt_nr = getBeltNrFromBeltElement(element);
8241   int belt_dir_nr = element - belt_base_element[belt_nr];
8242
8243   return (belt_dir_nr % 3);
8244 }
8245
8246 int getBeltDirNrFromBeltSwitchElement(int element)
8247 {
8248   static int belt_base_element[4] =
8249   {
8250     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8251     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8252     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8253     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8254   };
8255
8256   int belt_nr = getBeltNrFromBeltSwitchElement(element);
8257   int belt_dir_nr = element - belt_base_element[belt_nr];
8258
8259   return (belt_dir_nr % 3);
8260 }
8261
8262 int getBeltDirFromBeltElement(int element)
8263 {
8264   static int belt_move_dir[3] =
8265   {
8266     MV_LEFT,
8267     MV_NONE,
8268     MV_RIGHT
8269   };
8270
8271   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8272
8273   return belt_move_dir[belt_dir_nr];
8274 }
8275
8276 int getBeltDirFromBeltSwitchElement(int element)
8277 {
8278   static int belt_move_dir[3] =
8279   {
8280     MV_LEFT,
8281     MV_NONE,
8282     MV_RIGHT
8283   };
8284
8285   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8286
8287   return belt_move_dir[belt_dir_nr];
8288 }
8289
8290 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8291 {
8292   static int belt_base_element[4] =
8293   {
8294     EL_CONVEYOR_BELT_1_LEFT,
8295     EL_CONVEYOR_BELT_2_LEFT,
8296     EL_CONVEYOR_BELT_3_LEFT,
8297     EL_CONVEYOR_BELT_4_LEFT
8298   };
8299
8300   return belt_base_element[belt_nr] + belt_dir_nr;
8301 }
8302
8303 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8304 {
8305   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8306
8307   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8308 }
8309
8310 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8311 {
8312   static int belt_base_element[4] =
8313   {
8314     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8315     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8316     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8317     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8318   };
8319
8320   return belt_base_element[belt_nr] + belt_dir_nr;
8321 }
8322
8323 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8324 {
8325   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8326
8327   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8328 }
8329
8330 boolean swapTiles_EM(boolean is_pre_emc_cave)
8331 {
8332   return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8333 }
8334
8335 boolean getTeamMode_EM(void)
8336 {
8337   return game.team_mode || network_playing;
8338 }
8339
8340 boolean isActivePlayer_EM(int player_nr)
8341 {
8342   return stored_player[player_nr].active;
8343 }
8344
8345 unsigned int InitRND(int seed)
8346 {
8347   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8348     return InitEngineRandom_EM(seed);
8349   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8350     return InitEngineRandom_SP(seed);
8351   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8352     return InitEngineRandom_MM(seed);
8353   else
8354     return InitEngineRandom_RND(seed);
8355 }
8356
8357 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8358 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8359
8360 static int get_effective_element_EM(int tile, int frame_em)
8361 {
8362   int element             = object_mapping[tile].element_rnd;
8363   int action              = object_mapping[tile].action;
8364   boolean is_backside     = object_mapping[tile].is_backside;
8365   boolean action_removing = (action == ACTION_DIGGING ||
8366                              action == ACTION_SNAPPING ||
8367                              action == ACTION_COLLECTING);
8368
8369   if (frame_em < 7)
8370   {
8371     switch (tile)
8372     {
8373       case Xsplash_e:
8374       case Xsplash_w:
8375         return (frame_em > 5 ? EL_EMPTY : element);
8376
8377       default:
8378         return element;
8379     }
8380   }
8381   else  // frame_em == 7
8382   {
8383     switch (tile)
8384     {
8385       case Xsplash_e:
8386       case Xsplash_w:
8387         return EL_EMPTY;
8388
8389       case Ynut_stone:
8390         return EL_EMERALD;
8391
8392       case Ydiamond_stone:
8393         return EL_ROCK;
8394
8395       case Xdrip_stretch:
8396       case Xdrip_stretchB:
8397       case Ydrip_1_s:
8398       case Ydrip_1_sB:
8399       case Yball_1:
8400       case Xball_2:
8401       case Yball_2:
8402       case Yball_blank:
8403       case Ykey_1_blank:
8404       case Ykey_2_blank:
8405       case Ykey_3_blank:
8406       case Ykey_4_blank:
8407       case Ykey_5_blank:
8408       case Ykey_6_blank:
8409       case Ykey_7_blank:
8410       case Ykey_8_blank:
8411       case Ylenses_blank:
8412       case Ymagnify_blank:
8413       case Ygrass_blank:
8414       case Ydirt_blank:
8415       case Xsand_stonein_1:
8416       case Xsand_stonein_2:
8417       case Xsand_stonein_3:
8418       case Xsand_stonein_4:
8419         return element;
8420
8421       default:
8422         return (is_backside || action_removing ? EL_EMPTY : element);
8423     }
8424   }
8425 }
8426
8427 static boolean check_linear_animation_EM(int tile)
8428 {
8429   switch (tile)
8430   {
8431     case Xsand_stonesand_1:
8432     case Xsand_stonesand_quickout_1:
8433     case Xsand_sandstone_1:
8434     case Xsand_stonein_1:
8435     case Xsand_stoneout_1:
8436     case Xboom_1:
8437     case Xdynamite_1:
8438     case Ybug_w_n:
8439     case Ybug_n_e:
8440     case Ybug_e_s:
8441     case Ybug_s_w:
8442     case Ybug_e_n:
8443     case Ybug_s_e:
8444     case Ybug_w_s:
8445     case Ybug_n_w:
8446     case Ytank_w_n:
8447     case Ytank_n_e:
8448     case Ytank_e_s:
8449     case Ytank_s_w:
8450     case Ytank_e_n:
8451     case Ytank_s_e:
8452     case Ytank_w_s:
8453     case Ytank_n_w:
8454     case Xsplash_e:
8455     case Xsplash_w:
8456     case Ynut_stone:
8457       return TRUE;
8458   }
8459
8460   return FALSE;
8461 }
8462
8463 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8464                                      boolean has_crumbled_graphics,
8465                                      int crumbled, int sync_frame)
8466 {
8467   // if element can be crumbled, but certain action graphics are just empty
8468   // space (like instantly snapping sand to empty space in 1 frame), do not
8469   // treat these empty space graphics as crumbled graphics in EMC engine
8470   if (crumbled == IMG_EMPTY_SPACE)
8471     has_crumbled_graphics = FALSE;
8472
8473   if (has_crumbled_graphics)
8474   {
8475     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8476     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8477                                            g_crumbled->anim_delay,
8478                                            g_crumbled->anim_mode,
8479                                            g_crumbled->anim_start_frame,
8480                                            sync_frame);
8481
8482     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8483                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8484
8485     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8486     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8487
8488     g_em->has_crumbled_graphics = TRUE;
8489   }
8490   else
8491   {
8492     g_em->crumbled_bitmap = NULL;
8493     g_em->crumbled_src_x = 0;
8494     g_em->crumbled_src_y = 0;
8495     g_em->crumbled_border_size = 0;
8496     g_em->crumbled_tile_size = 0;
8497
8498     g_em->has_crumbled_graphics = FALSE;
8499   }
8500 }
8501
8502 #if 0
8503 void ResetGfxAnimation_EM(int x, int y, int tile)
8504 {
8505   GfxFrame[x][y] = 0;
8506 }
8507 #endif
8508
8509 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8510                         int tile, int frame_em, int x, int y)
8511 {
8512   int action = object_mapping[tile].action;
8513   int direction = object_mapping[tile].direction;
8514   int effective_element = get_effective_element_EM(tile, frame_em);
8515   int graphic = (direction == MV_NONE ?
8516                  el_act2img(effective_element, action) :
8517                  el_act_dir2img(effective_element, action, direction));
8518   struct GraphicInfo *g = &graphic_info[graphic];
8519   int sync_frame;
8520   boolean action_removing = (action == ACTION_DIGGING ||
8521                              action == ACTION_SNAPPING ||
8522                              action == ACTION_COLLECTING);
8523   boolean action_moving   = (action == ACTION_FALLING ||
8524                              action == ACTION_MOVING ||
8525                              action == ACTION_PUSHING ||
8526                              action == ACTION_EATING ||
8527                              action == ACTION_FILLING ||
8528                              action == ACTION_EMPTYING);
8529   boolean action_falling  = (action == ACTION_FALLING ||
8530                              action == ACTION_FILLING ||
8531                              action == ACTION_EMPTYING);
8532
8533   // special case: graphic uses "2nd movement tile" and has defined
8534   // 7 frames for movement animation (or less) => use default graphic
8535   // for last (8th) frame which ends the movement animation
8536   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8537   {
8538     action = ACTION_DEFAULT;    // (keep action_* unchanged for now)
8539     graphic = (direction == MV_NONE ?
8540                el_act2img(effective_element, action) :
8541                el_act_dir2img(effective_element, action, direction));
8542
8543     g = &graphic_info[graphic];
8544   }
8545
8546   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8547   {
8548     GfxFrame[x][y] = 0;
8549   }
8550   else if (action_moving)
8551   {
8552     boolean is_backside = object_mapping[tile].is_backside;
8553
8554     if (is_backside)
8555     {
8556       int direction = object_mapping[tile].direction;
8557       int move_dir = (action_falling ? MV_DOWN : direction);
8558
8559       GfxFrame[x][y]++;
8560
8561 #if 1
8562       // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8563       if (g->double_movement && frame_em == 0)
8564         GfxFrame[x][y] = 0;
8565 #endif
8566
8567       if (move_dir == MV_LEFT)
8568         GfxFrame[x - 1][y] = GfxFrame[x][y];
8569       else if (move_dir == MV_RIGHT)
8570         GfxFrame[x + 1][y] = GfxFrame[x][y];
8571       else if (move_dir == MV_UP)
8572         GfxFrame[x][y - 1] = GfxFrame[x][y];
8573       else if (move_dir == MV_DOWN)
8574         GfxFrame[x][y + 1] = GfxFrame[x][y];
8575     }
8576   }
8577   else
8578   {
8579     GfxFrame[x][y]++;
8580
8581     // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8582     if (tile == Xsand_stonesand_quickout_1 ||
8583         tile == Xsand_stonesand_quickout_2)
8584       GfxFrame[x][y]++;
8585   }
8586
8587   if (graphic_info[graphic].anim_global_sync)
8588     sync_frame = FrameCounter;
8589   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8590     sync_frame = GfxFrame[x][y];
8591   else
8592     sync_frame = 0;     // playfield border (pseudo steel)
8593
8594   SetRandomAnimationValue(x, y);
8595
8596   int frame = getAnimationFrame(g->anim_frames,
8597                                 g->anim_delay,
8598                                 g->anim_mode,
8599                                 g->anim_start_frame,
8600                                 sync_frame);
8601
8602   g_em->unique_identifier =
8603     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8604 }
8605
8606 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8607                                   int tile, int frame_em, int x, int y)
8608 {
8609   int action = object_mapping[tile].action;
8610   int direction = object_mapping[tile].direction;
8611   boolean is_backside = object_mapping[tile].is_backside;
8612   int effective_element = get_effective_element_EM(tile, frame_em);
8613   int effective_action = action;
8614   int graphic = (direction == MV_NONE ?
8615                  el_act2img(effective_element, effective_action) :
8616                  el_act_dir2img(effective_element, effective_action,
8617                                 direction));
8618   int crumbled = (direction == MV_NONE ?
8619                   el_act2crm(effective_element, effective_action) :
8620                   el_act_dir2crm(effective_element, effective_action,
8621                                  direction));
8622   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8623   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8624   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8625   struct GraphicInfo *g = &graphic_info[graphic];
8626   int sync_frame;
8627
8628   // special case: graphic uses "2nd movement tile" and has defined
8629   // 7 frames for movement animation (or less) => use default graphic
8630   // for last (8th) frame which ends the movement animation
8631   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8632   {
8633     effective_action = ACTION_DEFAULT;
8634     graphic = (direction == MV_NONE ?
8635                el_act2img(effective_element, effective_action) :
8636                el_act_dir2img(effective_element, effective_action,
8637                               direction));
8638     crumbled = (direction == MV_NONE ?
8639                 el_act2crm(effective_element, effective_action) :
8640                 el_act_dir2crm(effective_element, effective_action,
8641                                direction));
8642
8643     g = &graphic_info[graphic];
8644   }
8645
8646   if (graphic_info[graphic].anim_global_sync)
8647     sync_frame = FrameCounter;
8648   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8649     sync_frame = GfxFrame[x][y];
8650   else
8651     sync_frame = 0;     // playfield border (pseudo steel)
8652
8653   SetRandomAnimationValue(x, y);
8654
8655   int frame = getAnimationFrame(g->anim_frames,
8656                                 g->anim_delay,
8657                                 g->anim_mode,
8658                                 g->anim_start_frame,
8659                                 sync_frame);
8660
8661   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8662                       g->double_movement && is_backside);
8663
8664   // (updating the "crumbled" graphic definitions is probably not really needed,
8665   // as animations for crumbled graphics can't be longer than one EMC cycle)
8666   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8667                            sync_frame);
8668 }
8669
8670 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8671                                   int player_nr, int anim, int frame_em)
8672 {
8673   int element   = player_mapping[player_nr][anim].element_rnd;
8674   int action    = player_mapping[player_nr][anim].action;
8675   int direction = player_mapping[player_nr][anim].direction;
8676   int graphic = (direction == MV_NONE ?
8677                  el_act2img(element, action) :
8678                  el_act_dir2img(element, action, direction));
8679   struct GraphicInfo *g = &graphic_info[graphic];
8680   int sync_frame;
8681
8682   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8683
8684   stored_player[player_nr].StepFrame = frame_em;
8685
8686   sync_frame = stored_player[player_nr].Frame;
8687
8688   int frame = getAnimationFrame(g->anim_frames,
8689                                 g->anim_delay,
8690                                 g->anim_mode,
8691                                 g->anim_start_frame,
8692                                 sync_frame);
8693
8694   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8695                       &g_em->src_x, &g_em->src_y, FALSE);
8696 }
8697
8698 void InitGraphicInfo_EM(void)
8699 {
8700   int i, j, p;
8701
8702   // always start with reliable default values
8703   for (i = 0; i < GAME_TILE_MAX; i++)
8704   {
8705     object_mapping[i].element_rnd = EL_UNKNOWN;
8706     object_mapping[i].is_backside = FALSE;
8707     object_mapping[i].action = ACTION_DEFAULT;
8708     object_mapping[i].direction = MV_NONE;
8709   }
8710
8711   // always start with reliable default values
8712   for (p = 0; p < MAX_PLAYERS; p++)
8713   {
8714     for (i = 0; i < PLY_MAX; i++)
8715     {
8716       player_mapping[p][i].element_rnd = EL_UNKNOWN;
8717       player_mapping[p][i].action = ACTION_DEFAULT;
8718       player_mapping[p][i].direction = MV_NONE;
8719     }
8720   }
8721
8722   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8723   {
8724     int e = em_object_mapping_list[i].element_em;
8725
8726     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8727     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8728
8729     if (em_object_mapping_list[i].action != -1)
8730       object_mapping[e].action = em_object_mapping_list[i].action;
8731
8732     if (em_object_mapping_list[i].direction != -1)
8733       object_mapping[e].direction =
8734         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8735   }
8736
8737   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8738   {
8739     int a = em_player_mapping_list[i].action_em;
8740     int p = em_player_mapping_list[i].player_nr;
8741
8742     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8743
8744     if (em_player_mapping_list[i].action != -1)
8745       player_mapping[p][a].action = em_player_mapping_list[i].action;
8746
8747     if (em_player_mapping_list[i].direction != -1)
8748       player_mapping[p][a].direction =
8749         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8750   }
8751
8752   for (i = 0; i < GAME_TILE_MAX; i++)
8753   {
8754     int element = object_mapping[i].element_rnd;
8755     int action = object_mapping[i].action;
8756     int direction = object_mapping[i].direction;
8757     boolean is_backside = object_mapping[i].is_backside;
8758     boolean action_exploding = ((action == ACTION_EXPLODING ||
8759                                  action == ACTION_SMASHED_BY_ROCK ||
8760                                  action == ACTION_SMASHED_BY_SPRING) &&
8761                                 element != EL_DIAMOND);
8762     boolean action_active = (action == ACTION_ACTIVE);
8763     boolean action_other = (action == ACTION_OTHER);
8764
8765     for (j = 0; j < 8; j++)
8766     {
8767       int effective_element = get_effective_element_EM(i, j);
8768       int effective_action = (j < 7 ? action :
8769                               i == Xdrip_stretch ? action :
8770                               i == Xdrip_stretchB ? action :
8771                               i == Ydrip_1_s ? action :
8772                               i == Ydrip_1_sB ? action :
8773                               i == Yball_1 ? action :
8774                               i == Xball_2 ? action :
8775                               i == Yball_2 ? action :
8776                               i == Yball_blank ? action :
8777                               i == Ykey_1_blank ? action :
8778                               i == Ykey_2_blank ? action :
8779                               i == Ykey_3_blank ? action :
8780                               i == Ykey_4_blank ? action :
8781                               i == Ykey_5_blank ? action :
8782                               i == Ykey_6_blank ? action :
8783                               i == Ykey_7_blank ? action :
8784                               i == Ykey_8_blank ? action :
8785                               i == Ylenses_blank ? action :
8786                               i == Ymagnify_blank ? action :
8787                               i == Ygrass_blank ? action :
8788                               i == Ydirt_blank ? action :
8789                               i == Xsand_stonein_1 ? action :
8790                               i == Xsand_stonein_2 ? action :
8791                               i == Xsand_stonein_3 ? action :
8792                               i == Xsand_stonein_4 ? action :
8793                               i == Xsand_stoneout_1 ? action :
8794                               i == Xsand_stoneout_2 ? action :
8795                               i == Xboom_android ? ACTION_EXPLODING :
8796                               action_exploding ? ACTION_EXPLODING :
8797                               action_active ? action :
8798                               action_other ? action :
8799                               ACTION_DEFAULT);
8800       int graphic = (el_act_dir2img(effective_element, effective_action,
8801                                     direction));
8802       int crumbled = (el_act_dir2crm(effective_element, effective_action,
8803                                      direction));
8804       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8805       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8806       boolean has_action_graphics = (graphic != base_graphic);
8807       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8808       struct GraphicInfo *g = &graphic_info[graphic];
8809       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8810       Bitmap *src_bitmap;
8811       int src_x, src_y;
8812       // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8813       boolean special_animation = (action != ACTION_DEFAULT &&
8814                                    g->anim_frames == 3 &&
8815                                    g->anim_delay == 2 &&
8816                                    g->anim_mode & ANIM_LINEAR);
8817       int sync_frame = (i == Xdrip_stretch ? 7 :
8818                         i == Xdrip_stretchB ? 7 :
8819                         i == Ydrip_2_s ? j + 8 :
8820                         i == Ydrip_2_sB ? j + 8 :
8821                         i == Xacid_1 ? 0 :
8822                         i == Xacid_2 ? 10 :
8823                         i == Xacid_3 ? 20 :
8824                         i == Xacid_4 ? 30 :
8825                         i == Xacid_5 ? 40 :
8826                         i == Xacid_6 ? 50 :
8827                         i == Xacid_7 ? 60 :
8828                         i == Xacid_8 ? 70 :
8829                         i == Xfake_acid_1 ? 0 :
8830                         i == Xfake_acid_2 ? 10 :
8831                         i == Xfake_acid_3 ? 20 :
8832                         i == Xfake_acid_4 ? 30 :
8833                         i == Xfake_acid_5 ? 40 :
8834                         i == Xfake_acid_6 ? 50 :
8835                         i == Xfake_acid_7 ? 60 :
8836                         i == Xfake_acid_8 ? 70 :
8837                         i == Xfake_acid_1_player ? 0 :
8838                         i == Xfake_acid_2_player ? 10 :
8839                         i == Xfake_acid_3_player ? 20 :
8840                         i == Xfake_acid_4_player ? 30 :
8841                         i == Xfake_acid_5_player ? 40 :
8842                         i == Xfake_acid_6_player ? 50 :
8843                         i == Xfake_acid_7_player ? 60 :
8844                         i == Xfake_acid_8_player ? 70 :
8845                         i == Xball_2 ? 7 :
8846                         i == Yball_2 ? j + 8 :
8847                         i == Yball_blank ? j + 1 :
8848                         i == Ykey_1_blank ? j + 1 :
8849                         i == Ykey_2_blank ? j + 1 :
8850                         i == Ykey_3_blank ? j + 1 :
8851                         i == Ykey_4_blank ? j + 1 :
8852                         i == Ykey_5_blank ? j + 1 :
8853                         i == Ykey_6_blank ? j + 1 :
8854                         i == Ykey_7_blank ? j + 1 :
8855                         i == Ykey_8_blank ? j + 1 :
8856                         i == Ylenses_blank ? j + 1 :
8857                         i == Ymagnify_blank ? j + 1 :
8858                         i == Ygrass_blank ? j + 1 :
8859                         i == Ydirt_blank ? j + 1 :
8860                         i == Xamoeba_1 ? 0 :
8861                         i == Xamoeba_2 ? 1 :
8862                         i == Xamoeba_3 ? 2 :
8863                         i == Xamoeba_4 ? 3 :
8864                         i == Xamoeba_5 ? 0 :
8865                         i == Xamoeba_6 ? 1 :
8866                         i == Xamoeba_7 ? 2 :
8867                         i == Xamoeba_8 ? 3 :
8868                         i == Xexit_2 ? j + 8 :
8869                         i == Xexit_3 ? j + 16 :
8870                         i == Xdynamite_1 ? 0 :
8871                         i == Xdynamite_2 ? 8 :
8872                         i == Xdynamite_3 ? 16 :
8873                         i == Xdynamite_4 ? 24 :
8874                         i == Xsand_stonein_1 ? j + 1 :
8875                         i == Xsand_stonein_2 ? j + 9 :
8876                         i == Xsand_stonein_3 ? j + 17 :
8877                         i == Xsand_stonein_4 ? j + 25 :
8878                         i == Xsand_stoneout_1 && j == 0 ? 0 :
8879                         i == Xsand_stoneout_1 && j == 1 ? 0 :
8880                         i == Xsand_stoneout_1 && j == 2 ? 1 :
8881                         i == Xsand_stoneout_1 && j == 3 ? 2 :
8882                         i == Xsand_stoneout_1 && j == 4 ? 2 :
8883                         i == Xsand_stoneout_1 && j == 5 ? 3 :
8884                         i == Xsand_stoneout_1 && j == 6 ? 4 :
8885                         i == Xsand_stoneout_1 && j == 7 ? 4 :
8886                         i == Xsand_stoneout_2 && j == 0 ? 5 :
8887                         i == Xsand_stoneout_2 && j == 1 ? 6 :
8888                         i == Xsand_stoneout_2 && j == 2 ? 7 :
8889                         i == Xsand_stoneout_2 && j == 3 ? 8 :
8890                         i == Xsand_stoneout_2 && j == 4 ? 9 :
8891                         i == Xsand_stoneout_2 && j == 5 ? 11 :
8892                         i == Xsand_stoneout_2 && j == 6 ? 13 :
8893                         i == Xsand_stoneout_2 && j == 7 ? 15 :
8894                         i == Xboom_bug && j == 1 ? 2 :
8895                         i == Xboom_bug && j == 2 ? 2 :
8896                         i == Xboom_bug && j == 3 ? 4 :
8897                         i == Xboom_bug && j == 4 ? 4 :
8898                         i == Xboom_bug && j == 5 ? 2 :
8899                         i == Xboom_bug && j == 6 ? 2 :
8900                         i == Xboom_bug && j == 7 ? 0 :
8901                         i == Xboom_tank && j == 1 ? 2 :
8902                         i == Xboom_tank && j == 2 ? 2 :
8903                         i == Xboom_tank && j == 3 ? 4 :
8904                         i == Xboom_tank && j == 4 ? 4 :
8905                         i == Xboom_tank && j == 5 ? 2 :
8906                         i == Xboom_tank && j == 6 ? 2 :
8907                         i == Xboom_tank && j == 7 ? 0 :
8908                         i == Xboom_android && j == 7 ? 6 :
8909                         i == Xboom_1 && j == 1 ? 2 :
8910                         i == Xboom_1 && j == 2 ? 2 :
8911                         i == Xboom_1 && j == 3 ? 4 :
8912                         i == Xboom_1 && j == 4 ? 4 :
8913                         i == Xboom_1 && j == 5 ? 6 :
8914                         i == Xboom_1 && j == 6 ? 6 :
8915                         i == Xboom_1 && j == 7 ? 8 :
8916                         i == Xboom_2 && j == 0 ? 8 :
8917                         i == Xboom_2 && j == 1 ? 8 :
8918                         i == Xboom_2 && j == 2 ? 10 :
8919                         i == Xboom_2 && j == 3 ? 10 :
8920                         i == Xboom_2 && j == 4 ? 10 :
8921                         i == Xboom_2 && j == 5 ? 12 :
8922                         i == Xboom_2 && j == 6 ? 12 :
8923                         i == Xboom_2 && j == 7 ? 12 :
8924                         special_animation && j == 4 ? 3 :
8925                         effective_action != action ? 0 :
8926                         j);
8927       int frame = getAnimationFrame(g->anim_frames,
8928                                     g->anim_delay,
8929                                     g->anim_mode,
8930                                     g->anim_start_frame,
8931                                     sync_frame);
8932
8933       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8934                           g->double_movement && is_backside);
8935
8936       g_em->bitmap = src_bitmap;
8937       g_em->src_x = src_x;
8938       g_em->src_y = src_y;
8939       g_em->src_offset_x = 0;
8940       g_em->src_offset_y = 0;
8941       g_em->dst_offset_x = 0;
8942       g_em->dst_offset_y = 0;
8943       g_em->width  = TILEX;
8944       g_em->height = TILEY;
8945
8946       g_em->preserve_background = FALSE;
8947
8948       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8949                                sync_frame);
8950
8951       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8952                                    effective_action == ACTION_MOVING  ||
8953                                    effective_action == ACTION_PUSHING ||
8954                                    effective_action == ACTION_EATING)) ||
8955           (!has_action_graphics && (effective_action == ACTION_FILLING ||
8956                                     effective_action == ACTION_EMPTYING)))
8957       {
8958         int move_dir =
8959           (effective_action == ACTION_FALLING ||
8960            effective_action == ACTION_FILLING ||
8961            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8962         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8963         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
8964         int num_steps = (i == Ydrip_1_s  ? 16 :
8965                          i == Ydrip_1_sB ? 16 :
8966                          i == Ydrip_2_s  ? 16 :
8967                          i == Ydrip_2_sB ? 16 :
8968                          i == Xsand_stonein_1 ? 32 :
8969                          i == Xsand_stonein_2 ? 32 :
8970                          i == Xsand_stonein_3 ? 32 :
8971                          i == Xsand_stonein_4 ? 32 :
8972                          i == Xsand_stoneout_1 ? 16 :
8973                          i == Xsand_stoneout_2 ? 16 : 8);
8974         int cx = ABS(dx) * (TILEX / num_steps);
8975         int cy = ABS(dy) * (TILEY / num_steps);
8976         int step_frame = (i == Ydrip_2_s        ? j + 8 :
8977                           i == Ydrip_2_sB       ? j + 8 :
8978                           i == Xsand_stonein_2  ? j + 8 :
8979                           i == Xsand_stonein_3  ? j + 16 :
8980                           i == Xsand_stonein_4  ? j + 24 :
8981                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8982         int step = (is_backside ? step_frame : num_steps - step_frame);
8983
8984         if (is_backside)        // tile where movement starts
8985         {
8986           if (dx < 0 || dy < 0)
8987           {
8988             g_em->src_offset_x = cx * step;
8989             g_em->src_offset_y = cy * step;
8990           }
8991           else
8992           {
8993             g_em->dst_offset_x = cx * step;
8994             g_em->dst_offset_y = cy * step;
8995           }
8996         }
8997         else                    // tile where movement ends
8998         {
8999           if (dx < 0 || dy < 0)
9000           {
9001             g_em->dst_offset_x = cx * step;
9002             g_em->dst_offset_y = cy * step;
9003           }
9004           else
9005           {
9006             g_em->src_offset_x = cx * step;
9007             g_em->src_offset_y = cy * step;
9008           }
9009         }
9010
9011         g_em->width  = TILEX - cx * step;
9012         g_em->height = TILEY - cy * step;
9013       }
9014
9015       // create unique graphic identifier to decide if tile must be redrawn
9016       /* bit 31 - 16 (16 bit): EM style graphic
9017          bit 15 - 12 ( 4 bit): EM style frame
9018          bit 11 -  6 ( 6 bit): graphic width
9019          bit  5 -  0 ( 6 bit): graphic height */
9020       g_em->unique_identifier =
9021         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9022     }
9023   }
9024
9025   for (i = 0; i < GAME_TILE_MAX; i++)
9026   {
9027     for (j = 0; j < 8; j++)
9028     {
9029       int element = object_mapping[i].element_rnd;
9030       int action = object_mapping[i].action;
9031       int direction = object_mapping[i].direction;
9032       boolean is_backside = object_mapping[i].is_backside;
9033       int graphic_action  = el_act_dir2img(element, action, direction);
9034       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9035
9036       if ((action == ACTION_SMASHED_BY_ROCK ||
9037            action == ACTION_SMASHED_BY_SPRING ||
9038            action == ACTION_EATING) &&
9039           graphic_action == graphic_default)
9040       {
9041         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
9042                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9043                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
9044                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9045                  Xspring);
9046
9047         // no separate animation for "smashed by rock" -- use rock instead
9048         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9049         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9050
9051         g_em->bitmap            = g_xx->bitmap;
9052         g_em->src_x             = g_xx->src_x;
9053         g_em->src_y             = g_xx->src_y;
9054         g_em->src_offset_x      = g_xx->src_offset_x;
9055         g_em->src_offset_y      = g_xx->src_offset_y;
9056         g_em->dst_offset_x      = g_xx->dst_offset_x;
9057         g_em->dst_offset_y      = g_xx->dst_offset_y;
9058         g_em->width             = g_xx->width;
9059         g_em->height            = g_xx->height;
9060         g_em->unique_identifier = g_xx->unique_identifier;
9061
9062         if (!is_backside)
9063           g_em->preserve_background = TRUE;
9064       }
9065     }
9066   }
9067
9068   for (p = 0; p < MAX_PLAYERS; p++)
9069   {
9070     for (i = 0; i < PLY_MAX; i++)
9071     {
9072       int element = player_mapping[p][i].element_rnd;
9073       int action = player_mapping[p][i].action;
9074       int direction = player_mapping[p][i].direction;
9075
9076       for (j = 0; j < 8; j++)
9077       {
9078         int effective_element = element;
9079         int effective_action = action;
9080         int graphic = (direction == MV_NONE ?
9081                        el_act2img(effective_element, effective_action) :
9082                        el_act_dir2img(effective_element, effective_action,
9083                                       direction));
9084         struct GraphicInfo *g = &graphic_info[graphic];
9085         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9086         Bitmap *src_bitmap;
9087         int src_x, src_y;
9088         int sync_frame = j;
9089         int frame = getAnimationFrame(g->anim_frames,
9090                                       g->anim_delay,
9091                                       g->anim_mode,
9092                                       g->anim_start_frame,
9093                                       sync_frame);
9094
9095         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9096
9097         g_em->bitmap = src_bitmap;
9098         g_em->src_x = src_x;
9099         g_em->src_y = src_y;
9100         g_em->src_offset_x = 0;
9101         g_em->src_offset_y = 0;
9102         g_em->dst_offset_x = 0;
9103         g_em->dst_offset_y = 0;
9104         g_em->width  = TILEX;
9105         g_em->height = TILEY;
9106       }
9107     }
9108   }
9109 }
9110
9111 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9112                                        boolean any_player_moving,
9113                                        boolean any_player_snapping,
9114                                        boolean any_player_dropping)
9115 {
9116   if (frame == 7 && !any_player_dropping)
9117   {
9118     if (!local_player->was_waiting)
9119     {
9120       if (!CheckSaveEngineSnapshotToList())
9121         return;
9122
9123       local_player->was_waiting = TRUE;
9124     }
9125   }
9126   else if (any_player_moving || any_player_snapping || any_player_dropping)
9127   {
9128     local_player->was_waiting = FALSE;
9129   }
9130 }
9131
9132 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9133                                        boolean murphy_is_dropping)
9134 {
9135   if (murphy_is_waiting)
9136   {
9137     if (!local_player->was_waiting)
9138     {
9139       if (!CheckSaveEngineSnapshotToList())
9140         return;
9141
9142       local_player->was_waiting = TRUE;
9143     }
9144   }
9145   else
9146   {
9147     local_player->was_waiting = FALSE;
9148   }
9149 }
9150
9151 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9152                                        boolean button_released)
9153 {
9154   if (button_released)
9155   {
9156     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9157       CheckSaveEngineSnapshotToList();
9158   }
9159   else if (element_clicked)
9160   {
9161     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9162       CheckSaveEngineSnapshotToList();
9163
9164     game.snapshot.changed_action = TRUE;
9165   }
9166 }
9167
9168 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9169                                boolean any_player_moving,
9170                                boolean any_player_snapping,
9171                                boolean any_player_dropping)
9172 {
9173   if (tape.single_step && tape.recording && !tape.pausing)
9174     if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9175       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9176
9177   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9178                              any_player_snapping, any_player_dropping);
9179
9180   return tape.pausing;
9181 }
9182
9183 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9184                             boolean murphy_is_dropping)
9185 {
9186   boolean murphy_starts_dropping = FALSE;
9187   int i;
9188
9189   for (i = 0; i < MAX_PLAYERS; i++)
9190     if (stored_player[i].force_dropping)
9191       murphy_starts_dropping = TRUE;
9192
9193   if (tape.single_step && tape.recording && !tape.pausing)
9194     if (murphy_is_waiting && !murphy_starts_dropping)
9195       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9196
9197   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9198 }
9199
9200 void CheckSingleStepMode_MM(boolean element_clicked,
9201                             boolean button_released)
9202 {
9203   if (tape.single_step && tape.recording && !tape.pausing)
9204     if (button_released)
9205       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9206
9207   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9208 }
9209
9210 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9211                          int graphic, int sync_frame, int x, int y)
9212 {
9213   int frame = getGraphicAnimationFrame(graphic, sync_frame);
9214
9215   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9216 }
9217
9218 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9219 {
9220   return (IS_NEXT_FRAME(sync_frame, graphic));
9221 }
9222
9223 int getGraphicInfo_Delay(int graphic)
9224 {
9225   return graphic_info[graphic].anim_delay;
9226 }
9227
9228 void PlayMenuSoundExt(int sound)
9229 {
9230   if (sound == SND_UNDEFINED)
9231     return;
9232
9233   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9234       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9235     return;
9236
9237   if (IS_LOOP_SOUND(sound))
9238     PlaySoundLoop(sound);
9239   else
9240     PlaySound(sound);
9241 }
9242
9243 void PlayMenuSound(void)
9244 {
9245   PlayMenuSoundExt(menu.sound[game_status]);
9246 }
9247
9248 void PlayMenuSoundStereo(int sound, int stereo_position)
9249 {
9250   if (sound == SND_UNDEFINED)
9251     return;
9252
9253   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9254       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9255     return;
9256
9257   if (IS_LOOP_SOUND(sound))
9258     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9259   else
9260     PlaySoundStereo(sound, stereo_position);
9261 }
9262
9263 void PlayMenuSoundIfLoopExt(int sound)
9264 {
9265   if (sound == SND_UNDEFINED)
9266     return;
9267
9268   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9269       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9270     return;
9271
9272   if (IS_LOOP_SOUND(sound))
9273     PlaySoundLoop(sound);
9274 }
9275
9276 void PlayMenuSoundIfLoop(void)
9277 {
9278   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9279 }
9280
9281 void PlayMenuMusicExt(int music)
9282 {
9283   if (music == MUS_UNDEFINED)
9284     return;
9285
9286   if (!setup.sound_music)
9287     return;
9288
9289   if (IS_LOOP_MUSIC(music))
9290     PlayMusicLoop(music);
9291   else
9292     PlayMusic(music);
9293 }
9294
9295 void PlayMenuMusic(void)
9296 {
9297   char *curr_music = getCurrentlyPlayingMusicFilename();
9298   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9299
9300   if (!strEqual(curr_music, next_music))
9301     PlayMenuMusicExt(menu.music[game_status]);
9302 }
9303
9304 void PlayMenuSoundsAndMusic(void)
9305 {
9306   PlayMenuSound();
9307   PlayMenuMusic();
9308 }
9309
9310 static void FadeMenuSounds(void)
9311 {
9312   FadeSounds();
9313 }
9314
9315 static void FadeMenuMusic(void)
9316 {
9317   char *curr_music = getCurrentlyPlayingMusicFilename();
9318   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9319
9320   if (!strEqual(curr_music, next_music))
9321     FadeMusic();
9322 }
9323
9324 void FadeMenuSoundsAndMusic(void)
9325 {
9326   FadeMenuSounds();
9327   FadeMenuMusic();
9328 }
9329
9330 void PlaySoundActivating(void)
9331 {
9332 #if 0
9333   PlaySound(SND_MENU_ITEM_ACTIVATING);
9334 #endif
9335 }
9336
9337 void PlaySoundSelecting(void)
9338 {
9339 #if 0
9340   PlaySound(SND_MENU_ITEM_SELECTING);
9341 #endif
9342 }
9343
9344 void ToggleFullscreenIfNeeded(void)
9345 {
9346   // if setup and video fullscreen state are already matching, nothing do do
9347   if (setup.fullscreen == video.fullscreen_enabled ||
9348       !video.fullscreen_available)
9349     return;
9350
9351   SDLSetWindowFullscreen(setup.fullscreen);
9352
9353   // set setup value according to successfully changed fullscreen mode
9354   setup.fullscreen = video.fullscreen_enabled;
9355 }
9356
9357 void ChangeWindowScalingIfNeeded(void)
9358 {
9359   // if setup and video window scaling are already matching, nothing do do
9360   if (setup.window_scaling_percent == video.window_scaling_percent ||
9361       video.fullscreen_enabled)
9362     return;
9363
9364   SDLSetWindowScaling(setup.window_scaling_percent);
9365
9366   // set setup value according to successfully changed window scaling
9367   setup.window_scaling_percent = video.window_scaling_percent;
9368 }
9369
9370 void ChangeVsyncModeIfNeeded(void)
9371 {
9372   int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9373   int video_vsync_mode = video.vsync_mode;
9374
9375   // if setup and video vsync mode are already matching, nothing do do
9376   if (setup_vsync_mode == video_vsync_mode)
9377     return;
9378
9379   // if renderer is using OpenGL, vsync mode can directly be changed
9380   SDLSetScreenVsyncMode(setup.vsync_mode);
9381
9382   // if vsync mode unchanged, try re-creating renderer to set vsync mode
9383   if (video.vsync_mode == video_vsync_mode)
9384   {
9385     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9386
9387     // save backbuffer content which gets lost when re-creating screen
9388     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9389
9390     // force re-creating screen and renderer to set new vsync mode
9391     video.fullscreen_enabled = !setup.fullscreen;
9392
9393     // when creating new renderer, destroy textures linked to old renderer
9394     FreeAllImageTextures();     // needs old renderer to free the textures
9395
9396     // re-create screen and renderer (including change of vsync mode)
9397     ChangeVideoModeIfNeeded(setup.fullscreen);
9398
9399     // set setup value according to successfully changed fullscreen mode
9400     setup.fullscreen = video.fullscreen_enabled;
9401
9402     // restore backbuffer content from temporary backbuffer backup bitmap
9403     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9404     FreeBitmap(tmp_backbuffer);
9405
9406     // update visible window/screen
9407     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9408
9409     // when changing vsync mode, re-create textures for new renderer
9410     InitImageTextures();
9411   }
9412
9413   // set setup value according to successfully changed vsync mode
9414   setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9415 }
9416
9417 static void JoinRectangles(int *x, int *y, int *width, int *height,
9418                            int x2, int y2, int width2, int height2)
9419 {
9420   // do not join with "off-screen" rectangle
9421   if (x2 == -1 || y2 == -1)
9422     return;
9423
9424   *x = MIN(*x, x2);
9425   *y = MIN(*y, y2);
9426   *width = MAX(*width, width2);
9427   *height = MAX(*height, height2);
9428 }
9429
9430 void SetAnimStatus(int anim_status_new)
9431 {
9432   if (anim_status_new == GAME_MODE_MAIN)
9433     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9434   else if (anim_status_new == GAME_MODE_NAMES)
9435     anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9436   else if (anim_status_new == GAME_MODE_SCORES)
9437     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9438
9439   global.anim_status_next = anim_status_new;
9440
9441   // directly set screen modes that are entered without fading
9442   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
9443        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9444       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
9445        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9446       (global.anim_status      == GAME_MODE_PSEUDO_NAMESONLY &&
9447        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9448       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAMES &&
9449        global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9450     global.anim_status = global.anim_status_next;
9451 }
9452
9453 void SetGameStatus(int game_status_new)
9454 {
9455   if (game_status_new != game_status)
9456     game_status_last_screen = game_status;
9457
9458   game_status = game_status_new;
9459
9460   SetAnimStatus(game_status_new);
9461 }
9462
9463 void SetFontStatus(int game_status_new)
9464 {
9465   static int last_game_status = -1;
9466
9467   if (game_status_new != -1)
9468   {
9469     // set game status for font use after storing last game status
9470     last_game_status = game_status;
9471     game_status = game_status_new;
9472   }
9473   else
9474   {
9475     // reset game status after font use from last stored game status
9476     game_status = last_game_status;
9477   }
9478 }
9479
9480 void ResetFontStatus(void)
9481 {
9482   SetFontStatus(-1);
9483 }
9484
9485 void SetLevelSetInfo(char *identifier, int level_nr)
9486 {
9487   setString(&levelset.identifier, identifier);
9488
9489   levelset.level_nr = level_nr;
9490 }
9491
9492 boolean CheckIfAllViewportsHaveChanged(void)
9493 {
9494   // if game status has not changed, viewports have not changed either
9495   if (game_status == game_status_last)
9496     return FALSE;
9497
9498   // check if all viewports have changed with current game status
9499
9500   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9501   struct RectWithBorder *vp_door_1    = &viewport.door_1[game_status];
9502   struct RectWithBorder *vp_door_2    = &viewport.door_2[game_status];
9503   int new_real_sx       = vp_playfield->x;
9504   int new_real_sy       = vp_playfield->y;
9505   int new_full_sxsize   = vp_playfield->width;
9506   int new_full_sysize   = vp_playfield->height;
9507   int new_dx            = vp_door_1->x;
9508   int new_dy            = vp_door_1->y;
9509   int new_dxsize        = vp_door_1->width;
9510   int new_dysize        = vp_door_1->height;
9511   int new_vx            = vp_door_2->x;
9512   int new_vy            = vp_door_2->y;
9513   int new_vxsize        = vp_door_2->width;
9514   int new_vysize        = vp_door_2->height;
9515
9516   boolean playfield_viewport_has_changed =
9517     (new_real_sx != REAL_SX ||
9518      new_real_sy != REAL_SY ||
9519      new_full_sxsize != FULL_SXSIZE ||
9520      new_full_sysize != FULL_SYSIZE);
9521
9522   boolean door_1_viewport_has_changed =
9523     (new_dx != DX ||
9524      new_dy != DY ||
9525      new_dxsize != DXSIZE ||
9526      new_dysize != DYSIZE);
9527
9528   boolean door_2_viewport_has_changed =
9529     (new_vx != VX ||
9530      new_vy != VY ||
9531      new_vxsize != VXSIZE ||
9532      new_vysize != VYSIZE ||
9533      game_status_last == GAME_MODE_EDITOR);
9534
9535   return (playfield_viewport_has_changed &&
9536           door_1_viewport_has_changed &&
9537           door_2_viewport_has_changed);
9538 }
9539
9540 boolean CheckFadeAll(void)
9541 {
9542   return (CheckIfGlobalBorderHasChanged() ||
9543           CheckIfAllViewportsHaveChanged());
9544 }
9545
9546 void ChangeViewportPropertiesIfNeeded(void)
9547 {
9548   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9549                                FALSE : setup.small_game_graphics);
9550   int gfx_game_mode = game_status;
9551   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9552                         game_status);
9553   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
9554   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9555   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
9556   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
9557   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
9558   int new_win_xsize     = vp_window->width;
9559   int new_win_ysize     = vp_window->height;
9560   int border_left       = vp_playfield->border_left;
9561   int border_right      = vp_playfield->border_right;
9562   int border_top        = vp_playfield->border_top;
9563   int border_bottom     = vp_playfield->border_bottom;
9564   int new_sx            = vp_playfield->x      + border_left;
9565   int new_sy            = vp_playfield->y      + border_top;
9566   int new_sxsize        = vp_playfield->width  - border_left - border_right;
9567   int new_sysize        = vp_playfield->height - border_top  - border_bottom;
9568   int new_real_sx       = vp_playfield->x;
9569   int new_real_sy       = vp_playfield->y;
9570   int new_full_sxsize   = vp_playfield->width;
9571   int new_full_sysize   = vp_playfield->height;
9572   int new_dx            = vp_door_1->x;
9573   int new_dy            = vp_door_1->y;
9574   int new_dxsize        = vp_door_1->width;
9575   int new_dysize        = vp_door_1->height;
9576   int new_vx            = vp_door_2->x;
9577   int new_vy            = vp_door_2->y;
9578   int new_vxsize        = vp_door_2->width;
9579   int new_vysize        = vp_door_2->height;
9580   int new_ex            = vp_door_3->x;
9581   int new_ey            = vp_door_3->y;
9582   int new_exsize        = vp_door_3->width;
9583   int new_eysize        = vp_door_3->height;
9584   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9585   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9586                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9587   int new_scr_fieldx = new_sxsize / tilesize;
9588   int new_scr_fieldy = new_sysize / tilesize;
9589   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9590   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9591   boolean init_gfx_buffers = FALSE;
9592   boolean init_video_buffer = FALSE;
9593   boolean init_gadgets_and_anims = FALSE;
9594   boolean init_em_graphics = FALSE;
9595
9596   if (new_win_xsize != WIN_XSIZE ||
9597       new_win_ysize != WIN_YSIZE)
9598   {
9599     WIN_XSIZE = new_win_xsize;
9600     WIN_YSIZE = new_win_ysize;
9601
9602     init_video_buffer = TRUE;
9603     init_gfx_buffers = TRUE;
9604     init_gadgets_and_anims = TRUE;
9605
9606     // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9607   }
9608
9609   if (new_scr_fieldx != SCR_FIELDX ||
9610       new_scr_fieldy != SCR_FIELDY)
9611   {
9612     // this always toggles between MAIN and GAME when using small tile size
9613
9614     SCR_FIELDX = new_scr_fieldx;
9615     SCR_FIELDY = new_scr_fieldy;
9616
9617     // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9618   }
9619
9620   if (new_sx != SX ||
9621       new_sy != SY ||
9622       new_dx != DX ||
9623       new_dy != DY ||
9624       new_vx != VX ||
9625       new_vy != VY ||
9626       new_ex != EX ||
9627       new_ey != EY ||
9628       new_sxsize != SXSIZE ||
9629       new_sysize != SYSIZE ||
9630       new_dxsize != DXSIZE ||
9631       new_dysize != DYSIZE ||
9632       new_vxsize != VXSIZE ||
9633       new_vysize != VYSIZE ||
9634       new_exsize != EXSIZE ||
9635       new_eysize != EYSIZE ||
9636       new_real_sx != REAL_SX ||
9637       new_real_sy != REAL_SY ||
9638       new_full_sxsize != FULL_SXSIZE ||
9639       new_full_sysize != FULL_SYSIZE ||
9640       new_tilesize_var != TILESIZE_VAR
9641       )
9642   {
9643     // ------------------------------------------------------------------------
9644     // determine next fading area for changed viewport definitions
9645     // ------------------------------------------------------------------------
9646
9647     // start with current playfield area (default fading area)
9648     FADE_SX = REAL_SX;
9649     FADE_SY = REAL_SY;
9650     FADE_SXSIZE = FULL_SXSIZE;
9651     FADE_SYSIZE = FULL_SYSIZE;
9652
9653     // add new playfield area if position or size has changed
9654     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9655         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9656     {
9657       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9658                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9659     }
9660
9661     // add current and new door 1 area if position or size has changed
9662     if (new_dx != DX || new_dy != DY ||
9663         new_dxsize != DXSIZE || new_dysize != DYSIZE)
9664     {
9665       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9666                      DX, DY, DXSIZE, DYSIZE);
9667       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9668                      new_dx, new_dy, new_dxsize, new_dysize);
9669     }
9670
9671     // add current and new door 2 area if position or size has changed
9672     if (new_vx != VX || new_vy != VY ||
9673         new_vxsize != VXSIZE || new_vysize != VYSIZE)
9674     {
9675       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9676                      VX, VY, VXSIZE, VYSIZE);
9677       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9678                      new_vx, new_vy, new_vxsize, new_vysize);
9679     }
9680
9681     // ------------------------------------------------------------------------
9682     // handle changed tile size
9683     // ------------------------------------------------------------------------
9684
9685     if (new_tilesize_var != TILESIZE_VAR)
9686     {
9687       // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9688
9689       // changing tile size invalidates scroll values of engine snapshots
9690       FreeEngineSnapshotSingle();
9691
9692       // changing tile size requires update of graphic mapping for EM engine
9693       init_em_graphics = TRUE;
9694     }
9695
9696     SX = new_sx;
9697     SY = new_sy;
9698     DX = new_dx;
9699     DY = new_dy;
9700     VX = new_vx;
9701     VY = new_vy;
9702     EX = new_ex;
9703     EY = new_ey;
9704     SXSIZE = new_sxsize;
9705     SYSIZE = new_sysize;
9706     DXSIZE = new_dxsize;
9707     DYSIZE = new_dysize;
9708     VXSIZE = new_vxsize;
9709     VYSIZE = new_vysize;
9710     EXSIZE = new_exsize;
9711     EYSIZE = new_eysize;
9712     REAL_SX = new_real_sx;
9713     REAL_SY = new_real_sy;
9714     FULL_SXSIZE = new_full_sxsize;
9715     FULL_SYSIZE = new_full_sysize;
9716     TILESIZE_VAR = new_tilesize_var;
9717
9718     init_gfx_buffers = TRUE;
9719     init_gadgets_and_anims = TRUE;
9720
9721     // Debug("tools:viewport", "viewports: init_gfx_buffers");
9722     // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9723   }
9724
9725   if (init_gfx_buffers)
9726   {
9727     // Debug("tools:viewport", "init_gfx_buffers");
9728
9729     SCR_FIELDX = new_scr_fieldx_buffers;
9730     SCR_FIELDY = new_scr_fieldy_buffers;
9731
9732     InitGfxBuffers();
9733
9734     SCR_FIELDX = new_scr_fieldx;
9735     SCR_FIELDY = new_scr_fieldy;
9736
9737     SetDrawDeactivationMask(REDRAW_NONE);
9738     SetDrawBackgroundMask(REDRAW_FIELD);
9739   }
9740
9741   if (init_video_buffer)
9742   {
9743     // Debug("tools:viewport", "init_video_buffer");
9744
9745     FreeAllImageTextures();     // needs old renderer to free the textures
9746
9747     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9748     InitImageTextures();
9749   }
9750
9751   if (init_gadgets_and_anims)
9752   {
9753     // Debug("tools:viewport", "init_gadgets_and_anims");
9754
9755     InitGadgets();
9756     InitGlobalAnimations();
9757   }
9758
9759   if (init_em_graphics)
9760   {
9761     InitGraphicInfo_EM();
9762   }
9763 }