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