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