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