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