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