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