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