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