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