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