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