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