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