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