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