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