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