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