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